
//${CONFIG_BEGIN}
CFG_ANDROID_NATIVE_GL_ENABLED="1";
CFG_BINARY_FILES="*.bin|*.dat";
CFG_BRL_DATABUFFER_IMPLEMENTED="1";
CFG_BRL_GAMETARGET_IMPLEMENTED="1";
CFG_BRL_THREAD_IMPLEMENTED="1";
CFG_CONFIG="release";
CFG_HOST="winnt";
CFG_IMAGE_FILES="*.png|*.jpg";
CFG_LANG="js";
CFG_MINIB3D_DEBUG_MODEL="0";
CFG_MINIB3D_DRIVER="";
CFG_MOJO_AUTO_SUSPEND_ENABLED="1";
CFG_MOJO_DRIVER_IMPLEMENTED="1";
CFG_MUSIC_FILES="*.wav|*.ogg|*.mp3|*.m4a";
CFG_OPENGL_DEPTH_BUFFER_ENABLED="1";
CFG_OPENGL_GLES20_ENABLED="1";
CFG_SAFEMODE="0";
CFG_SOUND_FILES="*.wav|*.ogg|*.mp3|*.m4a";
CFG_TARGET="html5";
CFG_TEXT_FILES="*.txt|*.xml|*.xrf|*.xaf|*.xmf|*.xaf|*.xsf";
//${CONFIG_END}

//${METADATA_BEGIN}
var META_DATA="[mojo_font.png];type=image/png;width=864;height=13;\n";
//${METADATA_END}

//${TRANSCODE_BEGIN}

// Javascript Monkey runtime.
//
// Placed into the public domain 24/02/2011.
// No warranty implied; use at your own risk.

//***** JavaScript Runtime *****

var D2R=0.017453292519943295;
var R2D=57.29577951308232;

var err_info="";
var err_stack=[];

var dbg_index=0;

function push_err(){
	err_stack.push( err_info );
}

function pop_err(){
	err_info=err_stack.pop();
}

function stackTrace(){
	if( !err_info.length ) return "";
	var str=err_info+"\n";
	for( var i=err_stack.length-1;i>0;--i ){
		str+=err_stack[i]+"\n";
	}
	return str;
}

function print( str ){
	var cons=document.getElementById( "GameConsole" );
	if( cons ){
		cons.value+=str+"\n";
		cons.scrollTop=cons.scrollHeight-cons.clientHeight;
	}else if( window.console!=undefined ){
		window.console.log( str );
	}
	return 0;
}

function alertError( err ){
	if( typeof(err)=="string" && err=="" ) return;
	alert( "Monkey Runtime Error : "+err.toString()+"\n\n"+stackTrace() );
}

function error( err ){
	throw err;
}

function debugLog( str ){
	if( window.console!=undefined ) window.console.log( str );
}

function debugStop(){
	debugger;	//	error( "STOP" );
}

function dbg_object( obj ){
	if( obj ) return obj;
	error( "Null object access" );
}

function dbg_charCodeAt( str,index ){
	if( index<0 || index>=str.length ) error( "Character index out of range" );
	return str.charCodeAt( index );
}

function dbg_array( arr,index ){
	if( index<0 || index>=arr.length ) error( "Array index out of range" );
	dbg_index=index;
	return arr;
}

function new_bool_array( len ){
	var arr=Array( len );
	for( var i=0;i<len;++i ) arr[i]=false;
	return arr;
}

function new_number_array( len ){
	var arr=Array( len );
	for( var i=0;i<len;++i ) arr[i]=0;
	return arr;
}

function new_string_array( len ){
	var arr=Array( len );
	for( var i=0;i<len;++i ) arr[i]='';
	return arr;
}

function new_array_array( len ){
	var arr=Array( len );
	for( var i=0;i<len;++i ) arr[i]=[];
	return arr;
}

function new_object_array( len ){
	var arr=Array( len );
	for( var i=0;i<len;++i ) arr[i]=null;
	return arr;
}

function resize_bool_array( arr,len ){
	var i=arr.length;
	arr=arr.slice(0,len);
	if( len<=i ) return arr;
	arr.length=len;
	while( i<len ) arr[i++]=false;
	return arr;
}

function resize_number_array( arr,len ){
	var i=arr.length;
	arr=arr.slice(0,len);
	if( len<=i ) return arr;
	arr.length=len;
	while( i<len ) arr[i++]=0;
	return arr;
}

function resize_string_array( arr,len ){
	var i=arr.length;
	arr=arr.slice(0,len);
	if( len<=i ) return arr;
	arr.length=len;
	while( i<len ) arr[i++]="";
	return arr;
}

function resize_array_array( arr,len ){
	var i=arr.length;
	arr=arr.slice(0,len);
	if( len<=i ) return arr;
	arr.length=len;
	while( i<len ) arr[i++]=[];
	return arr;
}

function resize_object_array( arr,len ){
	var i=arr.length;
	arr=arr.slice(0,len);
	if( len<=i ) return arr;
	arr.length=len;
	while( i<len ) arr[i++]=null;
	return arr;
}

function string_compare( lhs,rhs ){
	var n=Math.min( lhs.length,rhs.length ),i,t;
	for( i=0;i<n;++i ){
		t=lhs.charCodeAt(i)-rhs.charCodeAt(i);
		if( t ) return t;
	}
	return lhs.length-rhs.length;
}

function string_replace( str,find,rep ){	//no unregex replace all?!?
	var i=0;
	for(;;){
		i=str.indexOf( find,i );
		if( i==-1 ) return str;
		str=str.substring( 0,i )+rep+str.substring( i+find.length );
		i+=rep.length;
	}
}

function string_trim( str ){
	var i=0,i2=str.length;
	while( i<i2 && str.charCodeAt(i)<=32 ) i+=1;
	while( i2>i && str.charCodeAt(i2-1)<=32 ) i2-=1;
	return str.slice( i,i2 );
}

function string_startswith( str,substr ){
	return substr.length<=str.length && str.slice(0,substr.length)==substr;
}

function string_endswith( str,substr ){
	return substr.length<=str.length && str.slice(str.length-substr.length,str.length)==substr;
}

function string_tochars( str ){
	var arr=new Array( str.length );
	for( var i=0;i<str.length;++i ) arr[i]=str.charCodeAt(i);
	return arr;
}

function string_fromchars( chars ){
	var str="",i;
	for( i=0;i<chars.length;++i ){
		str+=String.fromCharCode( chars[i] );
	}
	return str;
}

function object_downcast( obj,clas ){
	if( obj instanceof clas ) return obj;
	return null;
}

function object_implements( obj,iface ){
	if( obj && obj.implments && obj.implments[iface] ) return obj;
	return null;
}

function extend_class( clas ){
	var tmp=function(){};
	tmp.prototype=clas.prototype;
	return new tmp;
}

function ThrowableObject(){
}

ThrowableObject.prototype.toString=function(){ 
	return "Uncaught Monkey Exception"; 
}


function BBGameEvent(){}
BBGameEvent.KeyDown=1;
BBGameEvent.KeyUp=2;
BBGameEvent.KeyChar=3;
BBGameEvent.MouseDown=4;
BBGameEvent.MouseUp=5;
BBGameEvent.MouseMove=6;
BBGameEvent.TouchDown=7;
BBGameEvent.TouchUp=8;
BBGameEvent.TouchMove=9;
BBGameEvent.MotionAccel=10;

function BBGameDelegate(){}
BBGameDelegate.prototype.StartGame=function(){}
BBGameDelegate.prototype.SuspendGame=function(){}
BBGameDelegate.prototype.ResumeGame=function(){}
BBGameDelegate.prototype.UpdateGame=function(){}
BBGameDelegate.prototype.RenderGame=function(){}
BBGameDelegate.prototype.KeyEvent=function( ev,data ){}
BBGameDelegate.prototype.MouseEvent=function( ev,data,x,y ){}
BBGameDelegate.prototype.TouchEvent=function( ev,data,x,y ){}
BBGameDelegate.prototype.MotionEvent=function( ev,data,x,y,z ){}
BBGameDelegate.prototype.DiscardGraphics=function(){}

function BBDisplayMode( width,height ){
	this.width=width;
	this.height=height;
}

function BBGame(){
	BBGame._game=this;
	this._delegate=null;
	this._keyboardEnabled=false;
	this._updateRate=0;
	this._started=false;
	this._suspended=false;
	this._debugExs=(CFG_CONFIG=="debug");
	this._startms=Date.now();
}

BBGame.Game=function(){
	return BBGame._game;
}

BBGame.prototype.SetDelegate=function( delegate ){
	this._delegate=delegate;
}

BBGame.prototype.Delegate=function(){
	return this._delegate;
}

BBGame.prototype.SetUpdateRate=function( updateRate ){
	this._updateRate=updateRate;
}

BBGame.prototype.SetKeyboardEnabled=function( keyboardEnabled ){
	this._keyboardEnabled=keyboardEnabled;
}

BBGame.prototype.Started=function(){
	return this._started;
}

BBGame.prototype.Suspended=function(){
	return this._suspended;
}

BBGame.prototype.Millisecs=function(){
	return Date.now()-this._startms;
}

BBGame.prototype.GetDate=function( date ){
	var n=date.length;
	if( n>0 ){
		var t=new Date();
		date[0]=t.getFullYear();
		if( n>1 ){
			date[1]=t.getMonth()+1;
			if( n>2 ){
				date[2]=t.getDate();
				if( n>3 ){
					date[3]=t.getHours();
					if( n>4 ){
						date[4]=t.getMinutes();
						if( n>5 ){
							date[5]=t.getSeconds();
							if( n>6 ){
								date[6]=t.getMilliseconds();
							}
						}
					}
				}
			}
		}
	}
}

BBGame.prototype.SaveState=function( state ){
	localStorage.setItem( "monkeystate@"+document.URL,state );	//key can't start with dot in Chrome!
	return 1;
}

BBGame.prototype.LoadState=function(){
	var state=localStorage.getItem( "monkeystate@"+document.URL );
	if( state ) return state;
	return "";
}

BBGame.prototype.LoadString=function( path ){

	var xhr=new XMLHttpRequest();
	xhr.open( "GET",this.PathToUrl( path ),false );
	
	xhr.send( null );
	
	if( xhr.status==200 || xhr.status==0 ) return xhr.responseText;
	
	return "";
}

BBGame.prototype.PollJoystick=function( port,joyx,joyy,joyz,buttons ){
	return false;
}

BBGame.prototype.OpenUrl=function( url ){
	window.location=url;
}

BBGame.prototype.SetMouseVisible=function( visible ){
	if( visible ){
		this._canvas.style.cursor='default';	
	}else{
		this._canvas.style.cursor="url('%3D'), auto";
	}
}

BBGame.prototype.GetDeviceWidth=function(){
	return 0;
}

BBGame.prototype.GetDeviceHeight=function(){
	return 0;
}

BBGame.prototype.SetDeviceWindow=function( width,height,flags ){
}

BBGame.prototype.GetDisplayModes=function(){
	return new Array();
}

BBGame.prototype.GetDesktopMode=function(){
	return null;
}

BBGame.prototype.SetSwapInterval=function( interval ){
}

BBGame.prototype.PathToFilePath=function( path ){
	return "";
}

//***** js Game *****

BBGame.prototype.PathToUrl=function( path ){
	return path;
}

BBGame.prototype.LoadData=function( path ){

	var xhr=new XMLHttpRequest();
	xhr.open( "GET",this.PathToUrl( path ),false );

	if( xhr.overrideMimeType ) xhr.overrideMimeType( "text/plain; charset=x-user-defined" );

	xhr.send( null );
	if( xhr.status!=200 && xhr.status!=0 ) return null;

	var r=xhr.responseText;
	var buf=new ArrayBuffer( r.length );
	var bytes=new Int8Array( buf );
	for( var i=0;i<r.length;++i ){
		bytes[i]=r.charCodeAt( i );
	}
	return buf;
}

//***** INTERNAL ******

BBGame.prototype.Die=function( ex ){

	this._delegate=new BBGameDelegate();
	
	if( !ex.toString() ){
		return;
	}
	
	if( this._debugExs ){
		print( "Monkey Runtime Error : "+ex.toString() );
		print( stackTrace() );
	}
	
	throw ex;
}

BBGame.prototype.StartGame=function(){

	if( this._started ) return;
	this._started=true;
	
	if( this._debugExs ){
		try{
			this._delegate.StartGame();
		}catch( ex ){
			this.Die( ex );
		}
	}else{
		this._delegate.StartGame();
	}
}

BBGame.prototype.SuspendGame=function(){

	if( !this._started || this._suspended ) return;
	this._suspended=true;
	
	if( this._debugExs ){
		try{
			this._delegate.SuspendGame();
		}catch( ex ){
			this.Die( ex );
		}
	}else{
		this._delegate.SuspendGame();
	}
}

BBGame.prototype.ResumeGame=function(){

	if( !this._started || !this._suspended ) return;
	this._suspended=false;
	
	if( this._debugExs ){
		try{
			this._delegate.ResumeGame();
		}catch( ex ){
			this.Die( ex );
		}
	}else{
		this._delegate.ResumeGame();
	}
}

BBGame.prototype.UpdateGame=function(){

	if( !this._started || this._suspended ) return;

	if( this._debugExs ){
		try{
			this._delegate.UpdateGame();
		}catch( ex ){
			this.Die( ex );
		}	
	}else{
		this._delegate.UpdateGame();
	}
}

BBGame.prototype.RenderGame=function(){

	if( !this._started ) return;
	
	if( this._debugExs ){
		try{
			this._delegate.RenderGame();
		}catch( ex ){
			this.Die( ex );
		}	
	}else{
		this._delegate.RenderGame();
	}
}

BBGame.prototype.KeyEvent=function( ev,data ){

	if( !this._started ) return;
	
	if( this._debugExs ){
		try{
			this._delegate.KeyEvent( ev,data );
		}catch( ex ){
			this.Die( ex );
		}
	}else{
		this._delegate.KeyEvent( ev,data );
	}
}

BBGame.prototype.MouseEvent=function( ev,data,x,y ){

	if( !this._started ) return;
	
	if( this._debugExs ){
		try{
			this._delegate.MouseEvent( ev,data,x,y );
		}catch( ex ){
			this.Die( ex );
		}
	}else{
		this._delegate.MouseEvent( ev,data,x,y );
	}
}

BBGame.prototype.TouchEvent=function( ev,data,x,y ){

	if( !this._started ) return;
	
	if( this._debugExs ){
		try{
			this._delegate.TouchEvent( ev,data,x,y );
		}catch( ex ){
			this.Die( ex );
		}
	}else{
		this._delegate.TouchEvent( ev,data,x,y );
	}
}

BBGame.prototype.MotionEvent=function( ev,data,x,y,z ){

	if( !this._started ) return;
	
	if( this._debugExs ){
		try{
			this._delegate.MotionEvent( ev,data,x,y,z );
		}catch( ex ){
			this.Die( ex );
		}
	}else{
		this._delegate.MotionEvent( ev,data,x,y,z );
	}
}

BBGame.prototype.DiscardGraphics=function(){

	if( !this._started ) return;
	
	if( this._debugExs ){
		try{
			this._delegate.DiscardGraphics();
		}catch( ex ){
			this.Die( ex );
		}
	}else{
		this._delegate.DiscardGraphics();
	}
}


function BBHtml5Game( canvas ){
	BBGame.call( this );
	BBHtml5Game._game=this;
	this._canvas=canvas;
	this._loading=0;
	this._timerSeq=0;
	this._gl=null;
	if( CFG_OPENGL_GLES20_ENABLED=="1" ){
		this._gl=this._canvas.getContext( "webgl" );
		if( !this._gl ) this._gl=this._canvas.getContext( "experimental-webgl" );
		if( !this._gl ) this.Die( "Can't create WebGL" );
		gl=this._gl;
	}
}

BBHtml5Game.prototype=extend_class( BBGame );

BBHtml5Game.Html5Game=function(){
	return BBHtml5Game._game;
}

BBHtml5Game.prototype.ValidateUpdateTimer=function(){

	++this._timerSeq;
	if( this._suspended ) return;
	
	var game=this;
	var seq=game._timerSeq;
	
	var maxUpdates=4;
	var updateRate=this._updateRate;
	
	if( !updateRate ){

		var reqAnimFrame=(window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame);
	
		if( reqAnimFrame ){
			function animate(){
				if( seq!=game._timerSeq ) return;
	
				game.UpdateGame();
				if( seq!=game._timerSeq ) return;
	
				reqAnimFrame( animate );
				game.RenderGame();
			}
			reqAnimFrame( animate );
			return;
		}
		
		maxUpdates=1;
		updateRate=60;
	}
	
	var updatePeriod=1000.0/updateRate;
	var nextUpdate=0;

	function timeElapsed(){
		if( seq!=game._timerSeq ) return;
		
		if( !nextUpdate ) nextUpdate=Date.now();
		
		for( var i=0;i<maxUpdates;++i ){
		
			game.UpdateGame();
			if( seq!=game._timerSeq ) return;
			
			nextUpdate+=updatePeriod;
			var delay=nextUpdate-Date.now();
			
			if( delay>0 ){
				setTimeout( timeElapsed,delay );
				game.RenderGame();
				return;
			}
		}
		nextUpdate=0;
		setTimeout( timeElapsed,0 );
		game.RenderGame();
	}

	setTimeout( timeElapsed,0 );
}

//***** BBGame methods *****

BBHtml5Game.prototype.SetUpdateRate=function( updateRate ){

	BBGame.prototype.SetUpdateRate.call( this,updateRate );
	
	this.ValidateUpdateTimer();
}

BBHtml5Game.prototype.GetMetaData=function( path,key ){
	if( path.indexOf( "monkey://data/" )!=0 ) return "";
	path=path.slice(14);

	var i=META_DATA.indexOf( "["+path+"]" );
	if( i==-1 ) return "";
	i+=path.length+2;

	var e=META_DATA.indexOf( "\n",i );
	if( e==-1 ) e=META_DATA.length;

	i=META_DATA.indexOf( ";"+key+"=",i )
	if( i==-1 || i>=e ) return "";
	i+=key.length+2;

	e=META_DATA.indexOf( ";",i );
	if( e==-1 ) return "";

	return META_DATA.slice( i,e );
}

BBHtml5Game.prototype.PathToUrl=function( path ){
	if( path.indexOf( "monkey:" )!=0 ){
		return path;
	}else if( path.indexOf( "monkey://data/" )==0 ) {
		return "data/"+path.slice( 14 );
	}
	return "";
}

BBHtml5Game.prototype.GetLoading=function(){
	return this._loading;
}

BBHtml5Game.prototype.IncLoading=function(){
	++this._loading;
	return this._loading;
}

BBHtml5Game.prototype.DecLoading=function(){
	--this._loading;
	return this._loading;
}

BBHtml5Game.prototype.GetCanvas=function(){
	return this._canvas;
}

BBHtml5Game.prototype.GetWebGL=function(){
	return this._gl;
}

BBHtml5Game.prototype.GetDeviceWidth=function(){
	return this._canvas.width;
}

BBHtml5Game.prototype.GetDeviceHeight=function(){
	return this._canvas.height;
}

//***** INTERNAL *****

BBHtml5Game.prototype.UpdateGame=function(){

	if( !this._loading ) BBGame.prototype.UpdateGame.call( this );
}

BBHtml5Game.prototype.SuspendGame=function(){

	BBGame.prototype.SuspendGame.call( this );
	
	BBGame.prototype.RenderGame.call( this );
	
	this.ValidateUpdateTimer();
}

BBHtml5Game.prototype.ResumeGame=function(){

	BBGame.prototype.ResumeGame.call( this );
	
	this.ValidateUpdateTimer();
}

BBHtml5Game.prototype.Run=function(){

	var game=this;
	var canvas=game._canvas;
	
	var xscale=1;
	var yscale=1;
	
	var touchIds=new Array( 32 );
	for( i=0;i<32;++i ) touchIds[i]=-1;
	
	function eatEvent( e ){
		if( e.stopPropagation ){
			e.stopPropagation();
			e.preventDefault();
		}else{
			e.cancelBubble=true;
			e.returnValue=false;
		}
	}
	
	function keyToChar( key ){
		switch( key ){
		case 8:case 9:case 13:case 27:case 32:return key;
		case 33:case 34:case 35:case 36:case 37:case 38:case 39:case 40:case 45:return key|0x10000;
		case 46:return 127;
		}
		return 0;
	}
	
	function mouseX( e ){
		var x=e.clientX+document.body.scrollLeft;
		var c=canvas;
		while( c ){
			x-=c.offsetLeft;
			c=c.offsetParent;
		}
		return x*xscale;
	}
	
	function mouseY( e ){
		var y=e.clientY+document.body.scrollTop;
		var c=canvas;
		while( c ){
			y-=c.offsetTop;
			c=c.offsetParent;
		}
		return y*yscale;
	}

	function touchX( touch ){
		var x=touch.pageX;
		var c=canvas;
		while( c ){
			x-=c.offsetLeft;
			c=c.offsetParent;
		}
		return x;
	}			
	
	function touchY( touch ){
		var y=touch.pageY;
		var c=canvas;
		while( c ){
			y-=c.offsetTop;
			c=c.offsetParent;
		}
		return y;
	}
	
	canvas.onkeydown=function( e ){
		game.KeyEvent( BBGameEvent.KeyDown,e.keyCode );
		var chr=keyToChar( e.keyCode );
		if( chr ) game.KeyEvent( BBGameEvent.KeyChar,chr );
		if( e.keyCode<48 || (e.keyCode>111 && e.keyCode<122) ) eatEvent( e );
	}

	canvas.onkeyup=function( e ){
		game.KeyEvent( BBGameEvent.KeyUp,e.keyCode );
	}

	canvas.onkeypress=function( e ){
		if( e.charCode ){
			game.KeyEvent( BBGameEvent.KeyChar,e.charCode );
		}else if( e.which ){
			game.KeyEvent( BBGameEvent.KeyChar,e.which );
		}
	}

	canvas.onmousedown=function( e ){
		switch( e.button ){
		case 0:game.MouseEvent( BBGameEvent.MouseDown,0,mouseX(e),mouseY(e) );break;
		case 1:game.MouseEvent( BBGameEvent.MouseDown,2,mouseX(e),mouseY(e) );break;
		case 2:game.MouseEvent( BBGameEvent.MouseDown,1,mouseX(e),mouseY(e) );break;
		}
		eatEvent( e );
	}
	
	canvas.onmouseup=function( e ){
		switch( e.button ){
		case 0:game.MouseEvent( BBGameEvent.MouseUp,0,mouseX(e),mouseY(e) );break;
		case 1:game.MouseEvent( BBGameEvent.MouseUp,2,mouseX(e),mouseY(e) );break;
		case 2:game.MouseEvent( BBGameEvent.MouseUp,1,mouseX(e),mouseY(e) );break;
		}
		eatEvent( e );
	}
	
	canvas.onmousemove=function( e ){
		game.MouseEvent( BBGameEvent.MouseMove,-1,mouseX(e),mouseY(e) );
		eatEvent( e );
	}

	canvas.onmouseout=function( e ){
		game.MouseEvent( BBGameEvent.MouseUp,0,mouseX(e),mouseY(e) );
		game.MouseEvent( BBGameEvent.MouseUp,1,mouseX(e),mouseY(e) );
		game.MouseEvent( BBGameEvent.MouseUp,2,mouseX(e),mouseY(e) );
		eatEvent( e );
	}
	
	canvas.onclick=function( e ){
		if( game.Suspended() ){
			canvas.focus();
		}
		eatEvent( e );
		return;
	}
	
	canvas.oncontextmenu=function( e ){
		return false;
	}
	
	canvas.ontouchstart=function( e ){
		if( game.Suspended() ){
			canvas.focus();
		}
		for( var i=0;i<e.changedTouches.length;++i ){
			var touch=e.changedTouches[i];
			for( var j=0;j<32;++j ){
				if( touchIds[j]!=-1 ) continue;
				touchIds[j]=touch.identifier;
				game.TouchEvent( BBGameEvent.TouchDown,j,touchX(touch),touchY(touch) );
				break;
			}
		}
		eatEvent( e );
	}
	
	canvas.ontouchmove=function( e ){
		for( var i=0;i<e.changedTouches.length;++i ){
			var touch=e.changedTouches[i];
			for( var j=0;j<32;++j ){
				if( touchIds[j]!=touch.identifier ) continue;
				game.TouchEvent( BBGameEvent.TouchMove,j,touchX(touch),touchY(touch) );
				break;
			}
		}
		eatEvent( e );
	}
	
	canvas.ontouchend=function( e ){
		for( var i=0;i<e.changedTouches.length;++i ){
			var touch=e.changedTouches[i];
			for( var j=0;j<32;++j ){
				if( touchIds[j]!=touch.identifier ) continue;
				touchIds[j]=-1;
				game.TouchEvent( BBGameEvent.TouchUp,j,touchX(touch),touchY(touch) );
				break;
			}
		}
		eatEvent( e );
	}
	
	window.ondevicemotion=function( e ){
		var tx=e.accelerationIncludingGravity.x/9.81;
		var ty=e.accelerationIncludingGravity.y/9.81;
		var tz=e.accelerationIncludingGravity.z/9.81;
		var x,y;
		switch( window.orientation ){
		case   0:x=+tx;y=-ty;break;
		case 180:x=-tx;y=+ty;break;
		case  90:x=-ty;y=-tx;break;
		case -90:x=+ty;y=+tx;break;
		}
		game.MotionEvent( BBGameEvent.MotionAccel,0,x,y,tz );
		eatEvent( e );
	}

	canvas.onfocus=function( e ){
		if( CFG_MOJO_AUTO_SUSPEND_ENABLED=="1" ){
			game.ResumeGame();
		}
	}
	
	canvas.onblur=function( e ){
		for( var i=0;i<256;++i ) game.KeyEvent( BBGameEvent.KeyUp,i );
		if( CFG_MOJO_AUTO_SUSPEND_ENABLED=="1" ){
			game.SuspendGame();
		}
	}
	
	canvas.updateSize=function(){
		xscale=canvas.width/canvas.clientWidth;
		yscale=canvas.height/canvas.clientHeight;
		game.RenderGame();
	}
	
	canvas.updateSize();
	
	canvas.focus();
	
	game.StartGame();

	game.RenderGame();
}


function BBMonkeyGame( canvas ){
	BBHtml5Game.call( this,canvas );
}

BBMonkeyGame.prototype=extend_class( BBHtml5Game );

BBMonkeyGame.Main=function( canvas ){

	var game=new BBMonkeyGame( canvas );

	try{

		bbInit();
		bbMain();

	}catch( ex ){
	
		game.Die( ex );
		return;
	}

	if( !game.Delegate() ) return;
	
	game.Run();
}


// HTML5 mojo runtime.
//
// Copyright 2011 Mark Sibly, all rights reserved.
// No warranty implied; use at your own risk.

//***** gxtkGraphics class *****

function gxtkGraphics(){
	this.game=BBHtml5Game.Html5Game();
	this.canvas=this.game.GetCanvas()
	this.width=this.canvas.width;
	this.height=this.canvas.height;
	this.gl=null;
	this.gc=this.canvas.getContext( '2d' );
	this.tmpCanvas=null;
	this.r=255;
	this.b=255;
	this.g=255;
	this.white=true;
	this.color="rgb(255,255,255)"
	this.alpha=1;
	this.blend="source-over";
	this.ix=1;this.iy=0;
	this.jx=0;this.jy=1;
	this.tx=0;this.ty=0;
	this.tformed=false;
	this.scissorX=0;
	this.scissorY=0;
	this.scissorWidth=0;
	this.scissorHeight=0;
	this.clipped=false;
}

gxtkGraphics.prototype.BeginRender=function(){
	this.width=this.canvas.width;
	this.height=this.canvas.height;
	if( !this.gc ) return 0;
	this.gc.save();
	if( this.game.GetLoading() ) return 2;
	return 1;
}

gxtkGraphics.prototype.EndRender=function(){
	if( this.gc ) this.gc.restore();
}

gxtkGraphics.prototype.Width=function(){
	return this.width;
}

gxtkGraphics.prototype.Height=function(){
	return this.height;
}

gxtkGraphics.prototype.LoadSurface=function( path ){
	var game=this.game;

	var ty=game.GetMetaData( path,"type" );
	if( ty.indexOf( "image/" )!=0 ) return null;
	
	function onloadfun(){
		game.DecLoading();
	}
	
	game.IncLoading();

	var image=new Image();
	image.onload=onloadfun;
	image.meta_width=parseInt( game.GetMetaData( path,"width" ) );
	image.meta_height=parseInt( game.GetMetaData( path,"height" ) );
	image.src=game.PathToUrl( path );

	return new gxtkSurface( image,this );
}

gxtkGraphics.prototype.CreateSurface=function( width,height ){
	var canvas=document.createElement( 'canvas' );
	
	canvas.width=width;
	canvas.height=height;
	canvas.meta_width=width;
	canvas.meta_height=height;
	canvas.complete=true;
	
	var surface=new gxtkSurface( canvas,this );
	
	surface.gc=canvas.getContext( '2d' );
	
	return surface;
}

gxtkGraphics.prototype.SetAlpha=function( alpha ){
	this.alpha=alpha;
	this.gc.globalAlpha=alpha;
}

gxtkGraphics.prototype.SetColor=function( r,g,b ){
	this.r=r;
	this.g=g;
	this.b=b;
	this.white=(r==255 && g==255 && b==255);
	this.color="rgb("+(r|0)+","+(g|0)+","+(b|0)+")";
	this.gc.fillStyle=this.color;
	this.gc.strokeStyle=this.color;
}

gxtkGraphics.prototype.SetBlend=function( blend ){
	switch( blend ){
	case 1:
		this.blend="lighter";
		break;
	default:
		this.blend="source-over";
	}
	this.gc.globalCompositeOperation=this.blend;
}

gxtkGraphics.prototype.SetScissor=function( x,y,w,h ){
	this.scissorX=x;
	this.scissorY=y;
	this.scissorWidth=w;
	this.scissorHeight=h;
	this.clipped=(x!=0 || y!=0 || w!=this.canvas.width || h!=this.canvas.height);
	this.gc.restore();
	this.gc.save();
	if( this.clipped ){
		this.gc.beginPath();
		this.gc.rect( x,y,w,h );
		this.gc.clip();
		this.gc.closePath();
	}
	this.gc.fillStyle=this.color;
	this.gc.strokeStyle=this.color;	
	this.gc.globalAlpha=this.alpha;	
	this.gc.globalCompositeOperation=this.blend;
	if( this.tformed ) this.gc.setTransform( this.ix,this.iy,this.jx,this.jy,this.tx,this.ty );
}

gxtkGraphics.prototype.SetMatrix=function( ix,iy,jx,jy,tx,ty ){
	this.ix=ix;this.iy=iy;
	this.jx=jx;this.jy=jy;
	this.tx=tx;this.ty=ty;
	this.gc.setTransform( ix,iy,jx,jy,tx,ty );
	this.tformed=(ix!=1 || iy!=0 || jx!=0 || jy!=1 || tx!=0 || ty!=0);
}

gxtkGraphics.prototype.Cls=function( r,g,b ){
	this.gc.imageSmoothingEnabled = false;	
	this.gc.mozImageSmoothingEnabled = false;
	this.gc.oImageSmoothingEnabled = false;
	this.gc.webkitImageSmoothingEnabled = false;
	if( this.tformed ) this.gc.setTransform( 1,0,0,1,0,0 );
	this.gc.fillStyle="rgb("+(r|0)+","+(g|0)+","+(b|0)+")";
	this.gc.globalAlpha=1;
	this.gc.globalCompositeOperation="source-over";
	this.gc.fillRect( 0,0,this.canvas.width,this.canvas.height );
	this.gc.fillStyle=this.color;
	this.gc.globalAlpha=this.alpha;
	this.gc.globalCompositeOperation=this.blend;
	if( this.tformed ) this.gc.setTransform( this.ix,this.iy,this.jx,this.jy,this.tx,this.ty );
}

gxtkGraphics.prototype.DrawPoint=function( x,y ){
	if( this.tformed ){
		var px=x;
		x=px * this.ix + y * this.jx + this.tx;
		y=px * this.iy + y * this.jy + this.ty;
		this.gc.setTransform( 1,0,0,1,0,0 );
		this.gc.fillRect( x,y,1,1 );
		this.gc.setTransform( this.ix,this.iy,this.jx,this.jy,this.tx,this.ty );
	}else{
		this.gc.fillRect( x,y,1,1 );
	}
}

gxtkGraphics.prototype.DrawRect=function( x,y,w,h ){
	if( w<0 ){ x+=w;w=-w; }
	if( h<0 ){ y+=h;h=-h; }
	if( w<=0 || h<=0 ) return;
	//
	this.gc.fillRect( x,y,w,h );
}

gxtkGraphics.prototype.DrawLine=function( x1,y1,x2,y2 ){
	if( this.tformed ){
		var x1_t=x1 * this.ix + y1 * this.jx + this.tx;
		var y1_t=x1 * this.iy + y1 * this.jy + this.ty;
		var x2_t=x2 * this.ix + y2 * this.jx + this.tx;
		var y2_t=x2 * this.iy + y2 * this.jy + this.ty;
		this.gc.setTransform( 1,0,0,1,0,0 );
	  	this.gc.beginPath();
	  	this.gc.moveTo( x1_t,y1_t );
	  	this.gc.lineTo( x2_t,y2_t );
	  	this.gc.stroke();
	  	this.gc.closePath();
		this.gc.setTransform( this.ix,this.iy,this.jx,this.jy,this.tx,this.ty );
	}else{
	  	this.gc.beginPath();
	  	this.gc.moveTo( x1,y1 );
	  	this.gc.lineTo( x2,y2 );
	  	this.gc.stroke();
	  	this.gc.closePath();
	}
}

gxtkGraphics.prototype.DrawOval=function( x,y,w,h ){
	if( w<0 ){ x+=w;w=-w; }
	if( h<0 ){ y+=h;h=-h; }
	if( w<=0 || h<=0 ) return;
	//
  	var w2=w/2,h2=h/2;
	this.gc.save();
	this.gc.translate( x+w2,y+h2 );
	this.gc.scale( w2,h2 );
  	this.gc.beginPath();
	this.gc.arc( 0,0,1,0,Math.PI*2,false );
	this.gc.fill();
  	this.gc.closePath();
	this.gc.restore();
}

gxtkGraphics.prototype.DrawPoly=function( verts ){
	if( verts.length<2 ) return;
	this.gc.beginPath();
	this.gc.moveTo( verts[0],verts[1] );
	for( var i=2;i<verts.length;i+=2 ){
		this.gc.lineTo( verts[i],verts[i+1] );
	}
	this.gc.fill();
	this.gc.closePath();
}

gxtkGraphics.prototype.DrawPoly2=function( verts,surface,srx,srcy ){
	if( verts.length<4 ) return;
	this.gc.beginPath();
	this.gc.moveTo( verts[0],verts[1] );
	for( var i=4;i<verts.length;i+=4 ){
		this.gc.lineTo( verts[i],verts[i+1] );
	}
	this.gc.fill();
	this.gc.closePath();
}

gxtkGraphics.prototype.DrawSurface=function( surface,x,y ){
	if( !surface.image.complete ) return;
	
	if( this.white ){
		this.gc.drawImage( surface.image,x,y );
		return;
	}
	
	this.DrawImageTinted( surface.image,x,y,0,0,surface.swidth,surface.sheight );
}

gxtkGraphics.prototype.DrawSurface2=function( surface,x,y,srcx,srcy,srcw,srch ){
	if( !surface.image.complete ) return;

	if( srcw<0 ){ srcx+=srcw;srcw=-srcw; }
	if( srch<0 ){ srcy+=srch;srch=-srch; }
	if( srcw<=0 || srch<=0 ) return;

	if( this.white ){
		this.gc.drawImage( surface.image,srcx,srcy,srcw,srch,x,y,srcw,srch );
		return;
	}
	
	this.DrawImageTinted( surface.image,x,y,srcx,srcy,srcw,srch  );
}

gxtkGraphics.prototype.DrawImageTinted=function( image,dx,dy,sx,sy,sw,sh ){

	if( !this.tmpCanvas ){
		this.tmpCanvas=document.createElement( "canvas" );
	}

	if( sw>this.tmpCanvas.width || sh>this.tmpCanvas.height ){
		this.tmpCanvas.width=Math.max( sw,this.tmpCanvas.width );
		this.tmpCanvas.height=Math.max( sh,this.tmpCanvas.height );
	}
	
	var tmpGC=this.tmpCanvas.getContext( "2d" );
	tmpGC.globalCompositeOperation="copy";
	
	tmpGC.drawImage( image,sx,sy,sw,sh,0,0,sw,sh );
	
	var imgData=tmpGC.getImageData( 0,0,sw,sh );
	
	var p=imgData.data,sz=sw*sh*4,i;
	
	for( i=0;i<sz;i+=4 ){
		p[i]=p[i]*this.r/255;
		p[i+1]=p[i+1]*this.g/255;
		p[i+2]=p[i+2]*this.b/255;
	}
	
	tmpGC.putImageData( imgData,0,0 );
	
	this.gc.drawImage( this.tmpCanvas,0,0,sw,sh,dx,dy,sw,sh );
}

gxtkGraphics.prototype.ReadPixels=function( pixels,x,y,width,height,offset,pitch ){

	var imgData=this.gc.getImageData( x,y,width,height );
	
	var p=imgData.data,i=0,j=offset,px,py;
	
	for( py=0;py<height;++py ){
		for( px=0;px<width;++px ){
			pixels[j++]=(p[i+3]<<24)|(p[i]<<16)|(p[i+1]<<8)|p[i+2];
			i+=4;
		}
		j+=pitch-width;
	}
}

gxtkGraphics.prototype.WritePixels2=function( surface,pixels,x,y,width,height,offset,pitch ){

	if( !surface.gc ){
		if( !surface.image.complete ) return;
		var canvas=document.createElement( "canvas" );
		canvas.width=surface.swidth;
		canvas.height=surface.sheight;
		surface.gc=canvas.getContext( "2d" );
		surface.gc.globalCompositeOperation="copy";
		surface.gc.drawImage( surface.image,0,0 );
		surface.image=canvas;
	}

	var imgData=surface.gc.createImageData( width,height );

	var p=imgData.data,i=0,j=offset,px,py,argb;
	
	for( py=0;py<height;++py ){
		for( px=0;px<width;++px ){
			argb=pixels[j++];
			p[i]=(argb>>16) & 0xff;
			p[i+1]=(argb>>8) & 0xff;
			p[i+2]=argb & 0xff;
			p[i+3]=(argb>>24) & 0xff;
			i+=4;
		}
		j+=pitch-width;
	}
	
	surface.gc.putImageData( imgData,x,y );
}

//***** gxtkSurface class *****

function gxtkSurface( image,graphics ){
	this.image=image;
	this.graphics=graphics;
	this.swidth=image.meta_width;
	this.sheight=image.meta_height;
}

//***** GXTK API *****

gxtkSurface.prototype.Discard=function(){
	if( this.image ){
		this.image=null;
	}
}

gxtkSurface.prototype.Width=function(){
	return this.swidth;
}

gxtkSurface.prototype.Height=function(){
	return this.sheight;
}

gxtkSurface.prototype.Loaded=function(){
	return this.image.complete;
}

gxtkSurface.prototype.OnUnsafeLoadComplete=function(){
	return true;
}

//***** gxtkChannel class *****
function gxtkChannel(){
	this.sample=null;
	this.audio=null;
	this.volume=1;
	this.pan=0;
	this.rate=1;
	this.flags=0;
	this.state=0;
}

//***** gxtkAudio class *****
function gxtkAudio(){
	this.game=BBHtml5Game.Html5Game();
	this.okay=typeof(Audio)!="undefined";
	this.music=null;
	this.channels=new Array(33);
	for( var i=0;i<33;++i ){
		this.channels[i]=new gxtkChannel();
		if( !this.okay ) this.channels[i].state=-1;
	}
}

gxtkAudio.prototype.Suspend=function(){
	var i;
	for( i=0;i<33;++i ){
		var chan=this.channels[i];
		if( chan.state==1 ){
			if( chan.audio.ended && !chan.audio.loop ){
				chan.state=0;
			}else{
				chan.audio.pause();
				chan.state=3;
			}
		}
	}
}

gxtkAudio.prototype.Resume=function(){
	var i;
	for( i=0;i<33;++i ){
		var chan=this.channels[i];
		if( chan.state==3 ){
			chan.audio.play();
			chan.state=1;
		}
	}
}

gxtkAudio.prototype.LoadSample=function( path ){
	if( !this.okay ) return null;

	var audio=new Audio( this.game.PathToUrl( path ) );
	if( !audio ) return null;
	
	return new gxtkSample( audio );
}

gxtkAudio.prototype.PlaySample=function( sample,channel,flags ){
	if( !this.okay ) return;
	
	var chan=this.channels[channel];

	if( chan.state>0 ){
		chan.audio.pause();
		chan.state=0;
	}
	
	for( var i=0;i<33;++i ){
		var chan2=this.channels[i];
		if( chan2.state==1 && chan2.audio.ended && !chan2.audio.loop ) chan.state=0;
		if( chan2.state==0 && chan2.sample ){
			chan2.sample.FreeAudio( chan2.audio );
			chan2.sample=null;
			chan2.audio=null;
		}
	}

	var audio=sample.AllocAudio();
	if( !audio ) return;

	audio.loop=(flags&1)!=0;
	audio.volume=chan.volume;
	audio.play();

	chan.sample=sample;
	chan.audio=audio;
	chan.flags=flags;
	chan.state=1;
}

gxtkAudio.prototype.StopChannel=function( channel ){
	var chan=this.channels[channel];
	
	if( chan.state>0 ){
		chan.audio.pause();
		chan.state=0;
	}
}

gxtkAudio.prototype.PauseChannel=function( channel ){
	var chan=this.channels[channel];
	
	if( chan.state==1 ){
		if( chan.audio.ended && !chan.audio.loop ){
			chan.state=0;
		}else{
			chan.audio.pause();
			chan.state=2;
		}
	}
}

gxtkAudio.prototype.ResumeChannel=function( channel ){
	var chan=this.channels[channel];
	
	if( chan.state==2 ){
		chan.audio.play();
		chan.state=1;
	}
}

gxtkAudio.prototype.ChannelState=function( channel ){
	var chan=this.channels[channel];
	if( chan.state==1 && chan.audio.ended && !chan.audio.loop ) chan.state=0;
	if( chan.state==3 ) return 1;
	return chan.state;
}

gxtkAudio.prototype.SetVolume=function( channel,volume ){
	var chan=this.channels[channel];
	if( chan.state>0 ) chan.audio.volume=volume;
	chan.volume=volume;
}

gxtkAudio.prototype.SetPan=function( channel,pan ){
	var chan=this.channels[channel];
	chan.pan=pan;
}

gxtkAudio.prototype.SetRate=function( channel,rate ){
	var chan=this.channels[channel];
	chan.rate=rate;
}

gxtkAudio.prototype.PlayMusic=function( path,flags ){
	this.StopMusic();
	
	this.music=this.LoadSample( path );
	if( !this.music ) return;
	
	this.PlaySample( this.music,32,flags );
}

gxtkAudio.prototype.StopMusic=function(){
	this.StopChannel( 32 );

	if( this.music ){
		this.music.Discard();
		this.music=null;
	}
}

gxtkAudio.prototype.PauseMusic=function(){
	this.PauseChannel( 32 );
}

gxtkAudio.prototype.ResumeMusic=function(){
	this.ResumeChannel( 32 );
}

gxtkAudio.prototype.MusicState=function(){
	return this.ChannelState( 32 );
}

gxtkAudio.prototype.SetMusicVolume=function( volume ){
	this.SetVolume( 32,volume );
}

//***** gxtkSample class *****

function gxtkSample( audio ){
	this.audio=audio;
	this.free=new Array();
	this.insts=new Array();
}

gxtkSample.prototype.FreeAudio=function( audio ){
	this.free.push( audio );
}

gxtkSample.prototype.AllocAudio=function(){
	var audio;
	while( this.free.length ){
		audio=this.free.pop();
		try{
			audio.currentTime=0;
			return audio;
		}catch( ex ){
			print( "AUDIO ERROR1!" );
		}
	}
	
	//Max out?
	if( this.insts.length==8 ) return null;
	
	audio=new Audio( this.audio.src );
	
	//yucky loop handler for firefox!
	//
	audio.addEventListener( 'ended',function(){
		if( this.loop ){
			try{
				this.currentTime=0;
				this.play();
			}catch( ex ){
				print( "AUDIO ERROR2!" );
			}
		}
	},false );

	this.insts.push( audio );
	return audio;
}

gxtkSample.prototype.Discard=function(){
}


function BBThread(){
	this.result=null;
	this.running=false;
}

BBThread.prototype.Start=function(){
	this.result=null;
	this.running=true;
	this.Run__UNSAFE__();
}

BBThread.prototype.IsRunning=function(){
	return this.running;
}

BBThread.prototype.Result=function(){
	return this.result;
}

BBThread.prototype.Run__UNSAFE__=function(){
	this.running=false;
}


function BBAsyncImageLoaderThread(){
	this._running=false;
}

BBAsyncImageLoaderThread.prototype.Start=function(){

	var thread=this;
	var image=new Image();

	image.onload=function( e ){
		image.meta_width=image.width;
		image.meta_height=image.height;
		thread._surface=new gxtkSurface( image,thread._device )
		thread._running=false;
	}
	
	image.onerror=function( e ){
		thread._surface=null;
		thread._running=false;
	}
	
	thread._running=true;
	
	image.src=BBGame.Game().PathToUrl( thread._path );
}

BBAsyncImageLoaderThread.prototype.IsRunning=function(){
	return this._running;
}



function BBAsyncSoundLoaderThread(){
}

BBAsyncSoundLoaderThread.prototype.Start=function(){
	this._sample=this._device.LoadSample( this._path );
}

BBAsyncSoundLoaderThread.prototype.IsRunning=function(){
	return false;
}


function BBDataBuffer(){
	this.arrayBuffer=null;
	this.length=0;
}

BBDataBuffer.tbuf=new ArrayBuffer(4);
BBDataBuffer.tbytes=new Int8Array( BBDataBuffer.tbuf );
BBDataBuffer.tshorts=new Int16Array( BBDataBuffer.tbuf );
BBDataBuffer.tints=new Int32Array( BBDataBuffer.tbuf );
BBDataBuffer.tfloats=new Float32Array( BBDataBuffer.tbuf );

BBDataBuffer.prototype._Init=function( buffer ){
	this.arrayBuffer=buffer;
	this.length=buffer.byteLength;
	this.bytes=new Int8Array( buffer );	
	this.shorts=new Int16Array( buffer,0,this.length/2 );	
	this.ints=new Int32Array( buffer,0,this.length/4 );	
	this.floats=new Float32Array( buffer,0,this.length/4 );
}

BBDataBuffer.prototype._New=function( length ){
	if( this.arrayBuffer ) return false;
	
	var buf=new ArrayBuffer( length );
	if( !buf ) return false;
	
	this._Init( buf );
	return true;
}

BBDataBuffer.prototype._Load=function( path ){
	if( this.arrayBuffer ) return false;
	
	var buf=BBGame.Game().LoadData( path );
	if( !buf ) return false;
	
	this._Init( buf );
	return true;
}

BBDataBuffer.prototype._LoadAsync=function( path,thread ){

	var buf=this;
	
	var xhr=new XMLHttpRequest();
	xhr.open( "GET",BBGame.Game().PathToUrl( path ),true );
	xhr.responseType="arraybuffer";
	
	xhr.onload=function(e){
		if( this.status==200 || this.status==0 ){
			buf._Init( xhr.response );
			thread.result=buf;
		}
		thread.running=false;
	}
	
	xhr.onerror=function(e){
		thread.running=false;
	}
	
	xhr.send();
}


BBDataBuffer.prototype.GetArrayBuffer=function(){
	return this.arrayBuffer;
}

BBDataBuffer.prototype.Length=function(){
	return this.length;
}

BBDataBuffer.prototype.Discard=function(){
	if( this.arrayBuffer ){
		this.arrayBuffer=null;
		this.length=0;
	}
}

BBDataBuffer.prototype.PokeByte=function( addr,value ){
	this.bytes[addr]=value;
}

BBDataBuffer.prototype.PokeShort=function( addr,value ){
	if( addr&1 ){
		BBDataBuffer.tshorts[0]=value;
		this.bytes[addr]=BBDataBuffer.tbytes[0];
		this.bytes[addr+1]=BBDataBuffer.tbytes[1];
		return;
	}
	this.shorts[addr>>1]=value;
}

BBDataBuffer.prototype.PokeInt=function( addr,value ){
	if( addr&3 ){
		BBDataBuffer.tints[0]=value;
		this.bytes[addr]=BBDataBuffer.tbytes[0];
		this.bytes[addr+1]=BBDataBuffer.tbytes[1];
		this.bytes[addr+2]=BBDataBuffer.tbytes[2];
		this.bytes[addr+3]=BBDataBuffer.tbytes[3];
		return;
	}
	this.ints[addr>>2]=value;
}

BBDataBuffer.prototype.PokeFloat=function( addr,value ){
	if( addr&3 ){
		BBDataBuffer.tfloats[0]=value;
		this.bytes[addr]=BBDataBuffer.tbytes[0];
		this.bytes[addr+1]=BBDataBuffer.tbytes[1];
		this.bytes[addr+2]=BBDataBuffer.tbytes[2];
		this.bytes[addr+3]=BBDataBuffer.tbytes[3];
		return;
	}
	this.floats[addr>>2]=value;
}

BBDataBuffer.prototype.PeekByte=function( addr ){
	return this.bytes[addr];
}

BBDataBuffer.prototype.PeekShort=function( addr ){
	if( addr&1 ){
		BBDataBuffer.tbytes[0]=this.bytes[addr];
		BBDataBuffer.tbytes[1]=this.bytes[addr+1];
		return BBDataBuffer.tshorts[0];
	}
	return this.shorts[addr>>1];
}

BBDataBuffer.prototype.PeekInt=function( addr ){
	if( addr&3 ){
		BBDataBuffer.tbytes[0]=this.bytes[addr];
		BBDataBuffer.tbytes[1]=this.bytes[addr+1];
		BBDataBuffer.tbytes[2]=this.bytes[addr+2];
		BBDataBuffer.tbytes[3]=this.bytes[addr+3];
		return BBDataBuffer.tints[0];
	}
	return this.ints[addr>>2];
}

BBDataBuffer.prototype.PeekFloat=function( addr ){
	if( addr&3 ){
		BBDataBuffer.tbytes[0]=this.bytes[addr];
		BBDataBuffer.tbytes[1]=this.bytes[addr+1];
		BBDataBuffer.tbytes[2]=this.bytes[addr+2];
		BBDataBuffer.tbytes[3]=this.bytes[addr+3];
		return BBDataBuffer.tfloats[0];
	}
	return this.floats[addr>>2];
}


// Dodgy code to convert 'any' to i,f,iv,fv...
//
function _mkf( p ){
	if( typeof(p)=="boolean" ) return p?1.0:0.0;
	if( typeof(p)=="number" ) return p;
	return 0.0;
}

function _mki( p ){
	if( typeof(p)=="boolean" ) return p?1:0;
	if( typeof(p)=="number" ) return p|0;
	if( typeof(p)=="object" ) return p;
	return 0;
}

function _mkb( p ){
	if( typeof(p)=="boolean" ) return p;
	if( typeof(p)=="number" ) return p!=0;
	return false;
}

function _mkfv( p,params ){
	if( !params || !params.length ) return;
	if( (p instanceof Array) || (p instanceof Int32Array) || (p instanceof Float32Array) ){
		var n=Math.min( params.length,p.length );
		for( var i=0;i<n;++i ){
			params[i]=_mkf(p[i]);
		}
	}else{
		params[0]=_mkf(p);
	}
}

function _mkiv( p,params ){
	if( !params || !params.length ) return;
	if( (p instanceof Array) || (p instanceof Int32Array) || (p instanceof Float32Array) ){
		var n=Math.min( params.length,p.length );
		for( var i=0;i<n;++i ){
			params[i]=_mki(p[i]);
		}
	}else{
		params[0]=_mki(p);
	}
}

function _mkbv( p,params ){
	if( !params || !params.length ) return;
	if( (p instanceof Array) || (p instanceof Int32Array) || (p instanceof Float32Array) ){
		var n=Math.min( params.length,p.length );
		for( var i=0;i<n;++i ){
			params[i]=_mkb(p[i]);
		}
	}else{
		params[0]=_mkb(p);
	}
}

function _glBufferData( target,size,data,usage ){
	if( !data ){
		gl.bufferData( target,size,usage );
	}else if( size==data.size ){
		gl.bufferData( target,data.arrayBuffer,usage );
	}else{
		gl.bufferData( target,new Int8Array( data.arrayBuffer,0,size ),usage );
	}
}

function _glBufferSubData( target,offset,size,data ){
	if( size==data.size ){
		gl.bufferSubData( target,offset,data.arrayBuffer );
	}else{
		gl.bufferSubData( target,offset,new Int8Array( data.arrayBuffer,0,size ) );
	}
}


function _glClearDepthf( depth ){
	gl.clearDepth( depth );
}

function _glDepthRange( zNear,zFar ){
	gl.depthRange( zNear,zFar );
}

function _glGetActiveAttrib( program,index,type,size,name ){
	var info=gl.getActiveAttrib( program,index );
	if( type && type.length ) type[0]=info.type;
	if( size && size.length ) size[0]=info.size;
	if( name && name.length ) name[0]=info.name;
}

function _glGetActiveUniform( program,index,type,size,name ){
	var info=gl.getActiveUniform( program,index );
	if( type && type.length ) type[0]=info.type;
	if( size && size.length ) size[0]=info.size;
	if( name && name.length ) name[0]=info.name;
}

function _glGetAttachedShaders( program, maxcount, count, shaders ){
	var t=gl.getAttachedShaders();
	if( count && count.length ) count[0]=t.length;
	if( shaders ){
		var n=t.length;
		if( maxcount<n ) n=maxcount;
		if( shaders.length<n ) n=shaders.length;
		for( var i=0;i<n;++i ) shaders[i]=t[i];
	}
}

function _glGetBooleanv( pname,params ){
	_mkbv( gl.getParameter( pname ),params );
}

function _glGetBufferParameteriv( target, pname, params ){
	_mkiv( gl.glGetBufferParameter( target,pname ),params );
}

function _glGetFloatv( pname,params ){
	_mkfv( gl.getParameter( pname ),params );
}

function _glGetFramebufferAttachmentParameteriv( target, attachment, pname, params ){
	_mkiv( gl.getFrameBufferAttachmentParameter( target,attachment,pname ),params );
}

function _glGetIntegerv( pname, params ){
	_mkiv( gl.getParameter( pname ),params );
}

function _glGetProgramiv( program, pname, params ){
	_mkiv( gl.getProgramParameter( program,pname ),params );
}

function _glGetRenderbufferParameteriv( target, pname, params ){
	_mkiv( gl.getRenderbufferParameter( target,pname ),params );
}

function _glGetShaderiv( shader, pname, params ){
	_mkiv( gl.getShaderParameter( shader,pname ),params );
}

function _glGetString( pname ){
	var p=gl.getParameter( pname );
	if( typeof(p)=="string" ) return p;
	return "";
}

function _glGetTexParameterfv( target, pname, params ){
	_mkfv( gl.getTexParameter( target,pname ),params );
}

function _glGetTexParameteriv( target, pname, params ){
	_mkiv( gl.getTexParameter( target,pname ),params );
}

function _glGetUniformfv( program, location, params ){
	_mkfv( gl.getUniform( program,location ),params );
}

function _glGetUniformiv( program, location, params ){
	_mkiv( gl.getUniform( program,location ),params );
}

function _glGetUniformLocation( program, name ){
	var l=gl.getUniformLocation( program,name );
	if( l ) return l;
	return -1;
}

function _glGetVertexAttribfv( index, pname, params ){
	_mkfv( gl.getVertexAttrib( index,pname ),params );
}

function _glGetVertexAttribiv( index, pname, params ){
	_mkiv( gl.getVertexAttrib( index,pname ),params );
}

function _glReadPixels( x,y,width,height,format,type,pixels ){
	gl.readPixels( x,y,width,height,format,type,pixels.byteArray );
}

function _glBindBuffer( target,buffer ){
	if( buffer ){
		gl.bindBuffer( target,buffer );
	}else{
		gl.bindBuffer( target,null );
	}
}

function _glBindFramebuffer( target,framebuffer ){
	if( framebuffer ){
		gl.bindFramebuffer( target,framebuffer );
	}else{
		gl.bindFramebuffer( target,null );
	}
}

function _glBindRenderbuffer( target,renderbuffer ){
	if( renderbuffer ){
		gl.bindRenderbuffer( target,renderbuffer );
	}else{
		gl.bindRenderbuffer( target,null );
	}
}

function _glBindTexture( target,tex ){
	if( tex ){
		gl.bindTexture( target,tex );
		if( tex._loaded ){
			gl.texSubImage2D( target,tex._level,tex._xoffset,tex._yoffset,tex._format,tex._type,tex._loaded );
			if( tex._genmipmap ) gl.generateMipmap( target );
			tex._loaded=null;
			tex._loading=false;
		}
	}else{
		gl.bindTexture( target,null );
	}
}

function _glGenerateMipmap( target ){
	var tex=gl.getParameter( gl.TEXTURE_BINDING_2D );
	if( tex && tex._loading ){
		tex._genmipmap=true;
	}else{
		gl.generateMipmap( target );
	}
}

function _glTexImage2D( target,level,internalformat,format,type,path ){

	var game=BBHtml5Game.Html5Game();

	var ty=game.GetMetaData( path,"type" );
	if( ty.indexOf( "image/" )!=0 ) return null;
	
	var w=game.GetMetaData( path,"width" );
	var h=game.GetMetaData( path,"height" );

	var tex=gl.getParameter( gl.TEXTURE_BINDING_2D );
	tex._loaded=null;
	tex._loading=true;
	tex._genmipmap=false;
	tex._level=level;
	tex._xoffset=0;
	tex._yoffset=0;
	tex._format=format;
	tex._type=type;
	
	//Freaks Opera out!
	gl.texImage2D( target,level,internalformat,w,h,0,format,type,null );

	var img=new Image();
	
	function loaded(){
		tex._loaded=img;
	}
	
	img.onload=loaded;
	img.src=game.PathToUrl( path );
}

function _glTexImage2D2( target,level,internalformat,width,height,border,format,type,pixels ){
	gl.texImage2D( target,level,internalformat,width,height,border,format,type,pixels.byteArray );
}

function _glTexSubImage2D( target, level, xoffset, yoffset, format, type, path ){

	var game=BBHtml5Game.Html5Game();

	var ty=game.GetMetaData( path,"type" );
	if( ty.indexOf( "image/" )!=0 ) return null;

	var tex=gl.getParameter( gl.TEXTURE_BINDING_2D );
	tex._loaded=null;
	tex._loading=true;
	tex._genmipmap=false;
	tex._level=level;
	tex._xoffset=xoffset;
	tex._yoffset=yoffset;
	tex._format=format;
	tex._type=type;

	var img=new Image();
	
	function loaded(){
		tex._loaded=img;
	}
	
	img.onload=loaded;
	img.src=game.PathToUrl( path );
}

function _glTexSubImage2D2( target,level,xoffset,yoffset,width,height,format,type,pixels ){
	gl.texSubImage2D( target,level,xoffset,yoffset,width,height,format,type,pixels.byteArray );
}

function _glUniform1fv( location, count, v ){
	if( v.length==count ){
		gl.uniform1fv( location,v );
	}else{
		gl.uniform1fv( location,v.slice(0,cont) );
	}
}

function _glUniform1iv( location, count, v ){
	if( v.length==count ){
		gl.uniform1iv( location,v );
	}else{
		gl.uniform1iv( location,v.slice(0,cont) );
	}
}

function _glUniform2fv( location, count, v ){
	var n=count*2;
	if( v.length==n ){
		gl.uniform2fv( location,v );
	}else{
		gl.uniform2fv( location,v.slice(0,n) );
	}
}

function _glUniform2iv( location, count, v ){
	var n=count*2;
	if( v.length==n ){
		gl.uniform2iv( location,v );
	}else{
		gl.uniform2iv( location,v.slice(0,n) );
	}
}

function _glUniform3fv( location, count, v ){
	var n=count*3;
	if( v.length==n ){
		gl.uniform3fv( location,v );
	}else{
		gl.uniform3fv( location,v.slice(0,n) );
	}
}

function _glUniform3iv( location, count, v ){
	var n=count*3;
	if( v.length==n ){
		gl.uniform3iv( location,v );
	}else{
		gl.uniform3iv( location,v.slice(0,n) );
	}
}

function _glUniform4fv( location, count, v ){
	var n=count*4;
	if( v.length==n ){
		gl.uniform4fv( location,v );
	}else{
		gl.uniform4fv( location,v.slice(0,n) );
	}
}

function _glUniform4iv( location, count, v ){
	var n=count*4;
	if( v.length==n ){
		gl.uniform4iv( location,v );
	}else{
		gl.uniform4iv( location,v.slice(0,n) );
	}
}

function _glUniformMatrix2fv( location, count, transpose, value ){
	var n=count*4;
	if( value.length==n ){
		gl.uniformMatrix2fv( location,transpose,value );
	}else{
		gl.uniformMatrix2fv( location,transpose,value.slice(0,n) );
	}
}

function _glUniformMatrix3fv( location, count, transpose, value ){
	var n=count*9;
	if( value.length==n ){
		gl.uniformMatrix3fv( location,transpose,value );
	}else{
		gl.uniformMatrix3fv( location,transpose,value.slice(0,n) );
	}
}

function _glUniformMatrix4fv( location, count, transpose, value ){
	var n=count*16;
	if( value.length==n ){
		gl.uniformMatrix4fv( location,transpose,value );
	}else{
		gl.uniformMatrix4fv( location,transpose,value.slice(0,n) );
	}
}

//
//
// for minib3d html5

function EmptyNullClass(){}; //a hack to support mojo graphicsdevice


function LoadImageData(file, idx) {
	
	//load asynchronously
	//var preimage = new PreLoadImage();
	var image = document.createElement("img");
	var base = this;
	//isLoaded[0] =0;
	
	image.onload = function() {
		image.id = idx;
		//print("idload "+idx);
		
	};
	image.onerror = function() {
		image.id=0;
		//print ("image load error function");
	};

//print ("tpixmap: "+idx+" "+file);		
	image.filename = file;
	image.id =-1;
	image.src = file;

	
	return image;
};


function CheckIsLoaded(image) {
	if (image.id>-1) return true;
	return false;
}

function CreateImageData(w, h) {

	var image = document.createElement("img");
	//white 1x1 image gif
	image.src = "";

	image= HTMLResizePixmap(image,w,h,false);

	return image;
}

function HTMLResizePixmap(image,w,h, smooth) {

    var canvas = document.createElement("canvas");
	var ctx = canvas.getContext("2d");
	
	ctx.imageSmoothingEnabled = smooth;
	ctx.webkitImageSmoothingEnabled = smooth;
	ctx.mozImageSmoothingEnabled = smooth;
	
	//if (w>image.width || h>image.height) {
		//ctx.imageSmoothingEnabled = false;
	//}
	
	canvas.width = w;
	canvas.height = h;
	ctx.clearRect( 0, 0, w, h);
	ctx.drawImage(image, 0, 0, w, h);
	
	//return ctx.getImageData(0,0,w,h);
	image = canvas;

	return image;

}

function HTMLMaskPixmap(image, r,g,b) {
	
	var canvas = document.createElement("canvas");
	var ctx = canvas.getContext("2d");
	ctx.imageSmoothingEnabled = false;
	canvas.width = image.width; canvas.height = image.height;
	
	//ctx.fillRect(0,0, image.width, image.height);
	ctx.drawImage(image, 0, 0);
    var imageData = ctx.getImageData(0, 0, image.width, image.height);
    
	for (var i=0; i<imageData.data.length; i=i+4) {
		if ((imageData.data[i] == r) && (imageData.data[i+1] == g) && (imageData.data[i+2] == b)) {
			imageData.data[i+3] = 0; //turn alpha off
		}
	}
	
	ctx.putImageData(imageData,0,0);
	//image = canvas;
	return canvas;
}



function GetImageInfo( image ) {

	//print("image w/h "+image.width+" "+image.height);
	if (!CheckIsLoaded(image)) return [0,0];
	
	return [image.width, image.height];
	
};


//
// -- pixel read/write functions
//

var _pixelMod= new pixelMod();

function pixelMod() {
	//this.image_cache;
	this.image_cacheread;
	this.imagedata_cache_minib3d;
	
	this.image_cache_canvas ;//= document.createElement("canvas");
	this.image_cache_cxt ;//= this.image_cache_canvas.getContext("2d");
};

pixelMod.prototype.ReadPixel = function( image, x, y) {

	
	if (!(image === this.image_cache_canvas)) {
	
		this.image_cache_canvas = document.createElement("canvas");
		this.image_cache_cxt = this.image_cache_canvas.getContext("2d");
		this.image_cache_canvas.width = image.width; this.image_cache_canvas.height = image.height;
		this.image_cache_cxt.drawImage(image, 0, 0);
		//this.imagedata_cache_minib3d = this.image_cache_cxt.getImageData(0, 0, image.width, image.height);
		//this.image_cache = image;
		
	}
	//var i = (x+y*image.width)*4;
	//return (this.imagedata_cache_minib3d.data[i]|this.imagedata_cache_minib3d.data[i+1]|this.imagedata_cache_minib3d.data[i+2]|this.imagedata_cache_minib3d.data[i+3]);
	this.image_cache_cxt.drawImage(image, 0, 0);
	var p = this.image_cache_cxt.getImageData(x, y, 1, 1);
	return (p.data[0] << 24 |p.data[1] << 16|p.data[2]<<8|p.data[3]);
};

pixelMod.prototype.WritePixel = function( image, x, y, r,g,b,a) {
	
	if (!(image === this.image_cache_canvas)) {

		this.image_cache_canvas = document.createElement("canvas");
		this.image_cache_cxt = this.image_cache_canvas.getContext("2d");
		this.image_cache_canvas.width = image.width; this.image_cache_canvas.height = image.height;
		this.image_cache_cxt.drawImage(image, 0, 0);
		//this.imagedata_cache_minib3d = this.image_cache_cxt.getImageData(0, 0, image.width, image.height);
		//this.image_cache = image;

	}
	
	/*var i = (x+y*image.width)*4;

	this.imagedata_cache_minib3d.data[i]=r;
	this.imagedata_cache_minib3d.data[i+1]=g;
	this.imagedata_cache_minib3d.data[i+2]=b;
	this.imagedata_cache_minib3d.data[i+3]=a;*/
	
	this.image_cache_cxt.fillStyle = "rgba("+r+","+g+","+b+","+a+")";
	this.image_cache_cxt.fillRect (x,y,1,1);
	
	return this.image_cache_canvas;
};



function CheckWebGLContext () {
	test_gl = null;

	try {
		var canvas = document.createElement("canvas");
		// Try to grab the standard context. If it fails, fallback to experimental.
		test_gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
		canvas = null;
	}
	catch(e) {}
	
	if (test_gl) return 1;
	return 0;
}
/*****************************************************************************
* @preserve Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* @preserve
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@namespace Cal3D

	The namespace for all Cal3D classes and global methods.
*/
var Cal3D = Cal3D || {};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalMatrix
*/
Cal3D.CalMatrix = function() {
	this.dxdx = 1; this.dydx = 0; this.dzdx = 0;
	this.dxdy = 0; this.dydy = 1; this.dzdy = 0;
	this.dxdz = 0; this.dydz = 0; this.dzdz = 1;

	this.assign.apply(this, arguments);
};

/**
	Directly assign elements of the matrix, or copy elements from another matrix, or calculate elements form a quaternion.<br />
*/
Cal3D.CalMatrix.prototype.assign = function() {
	if(arguments.length == 1) {
		if(arguments[0] instanceof Cal3D.CalMatrix) {
			var other = arguments[0];
			this.dxdx = other.dxdx; this.dydx = other.dydx; this.dzdx = other.dzdx;
			this.dxdy = other.dxdy; this.dydy = other.dydy; this.dzdy = other.dzdy;
			this.dxdz = other.dxdz; this.dydz = other.dydz; this.dzdz = other.dzdz;
		}
		else if(arguments[0] instanceof Cal3D.CalQuaternion) {
			var q = arguments[0];
			var xx2 = q.x * q.x * 2;
			var yy2 = q.y * q.y * 2;
			var zz2 = q.z * q.z * 2;
			var xy2 = q.x * q.y * 2;
			var zw2 = q.z * q.w * 2;
			var xz2 = q.x * q.z * 2;
			var yw2 = q.y * q.w * 2;
			var yz2 = q.y * q.z * 2;
			var xw2 = q.x * q.w * 2;
			this.dxdx = 1-yy2-zz2;   this.dxdy =   xy2+zw2;  this.dxdz =   xz2-yw2;
			this.dydx =   xy2-zw2;   this.dydy = 1-xx2-zz2;  this.dydz =   yz2+xw2;
			this.dzdx =   xz2+yw2;   this.dzdy =   yz2-xw2;  this.dzdz = 1-xx2-yy2;
		}
	}
	else if(arguments.length == 2) {
		var weight = arguments[0];
		var m = arguments[1];
		this.dxdx = m.dxdx * weight;
		this.dxdy = m.dxdy * weight;
		this.dxdz = m.dxdz * weight;
		this.dydx = m.dydx * weight;
		this.dydy = m.dydy * weight;
		this.dydz = m.dydz * weight;
		this.dzdx = m.dzdx * weight;
		this.dzdy = m.dzdy * weight;
		this.dzdz = m.dzdz * weight;
	}
	else if(arguments.length == 9) {
		this.dxdx = arguments[0]; this.dydx = arguments[1]; this.dzdx = arguments[2];
		this.dxdy = arguments[3]; this.dydy = arguments[4]; this.dzdy = arguments[5];
		this.dxdz = arguments[6]; this.dydz = arguments[7]; this.dzdz = arguments[8];
	}

	return this;
};

/**
	Multiply another matrix to this matrix.
*/
Cal3D.CalMatrix.prototype.multMatrixLocal = function(m) {
	var ndxdx = m.dxdx*this.dxdx+m.dxdy*this.dydx+m.dxdz*this.dzdx;
	var ndydx = m.dydx*this.dxdx+m.dydy*this.dydx+m.dydz*this.dzdx;
	var ndzdx = m.dzdx*this.dxdx+m.dzdy*this.dydx+m.dzdz*this.dzdx;

	var ndxdy = m.dxdx*this.dxdy+m.dxdy*this.dydy+m.dxdz*this.dzdy;
	var ndydy = m.dydx*this.dxdy+m.dydy*this.dydy+m.dydz*this.dzdy;
	var ndzdy = m.dzdx*this.dxdy+m.dzdy*this.dydy+m.dzdz*this.dzdy;

	var ndxdz = m.dxdx*this.dxdz+m.dxdy*this.dydz+m.dxdz*this.dzdz;
	var ndydz = m.dydx*this.dxdz+m.dydy*this.dydz+m.dydz*this.dzdz;
	var ndzdz = m.dzdx*this.dxdz+m.dzdy*this.dydz+m.dzdz*this.dzdz;

	this.dxdx = ndxdx;
	this.dydx = ndydx;
	this.dzdx = ndzdx;
	this.dxdy = ndxdy;
	this.dydy = ndydy;
	this.dzdy = ndzdy;
	this.dxdz = ndxdz;
	this.dydz = ndydz;
	this.dzdz = ndzdz;

	return this;
};

/**
	Multiply a scalar factor to the matrix.
*/
Cal3D.CalMatrix.prototype.multScalarLocal = function(factor) {
	this.dxdx *= factor;
	this.dydx *= factor;
	this.dzdx *= factor;
	this.dxdy *= factor;
	this.dydy *= factor;
	this.dzdy *= factor;
	this.dxdz *= factor;
	this.dydz *= factor;
	this.dzdz *= factor;

	return this;
};

/**
	Add a weight times another matrix to this. matrix.
*/
Cal3D.CalMatrix.prototype.blend = function(factor, m) {
	this.dxdx += m.dxdx*factor;
	this.dydx += m.dydx*factor;
	this.dzdx += m.dzdx*factor;
	this.dxdy += m.dxdy*factor;
	this.dydy += m.dydy*factor;
	this.dzdy += m.dzdy*factor;
	this.dxdz += m.dxdz*factor;
	this.dydz += m.dydz*factor;
	this.dzdz += m.dzdz*factor;
};

/**
	Calculate determinant of the matrix.
*/
Cal3D.CalMatrix.prototype.det = function() {
	return    this.dxdx * (this.dydy*this.dzdz-this.dydz*this.dzdy)
			- this.dxdy * (this.dydx*this.dzdz-this.dzdx*this.dydz)
			+ this.dxdz * (this.dydx*this.dzdy-this.dzdx*this.dydy);
};



/**
	@class CalQuaternion
*/
Cal3D.CalQuaternion = function() {
	this.x = 0;
	this.y = 0;
	this.z = 0;
	this.w = 1;

	this.assign.apply(this, arguments);
};

/**
	Directly assign components of the quaternion, or copy components from another quaternion.
*/
Cal3D.CalQuaternion.prototype.assign = function() {
	if(arguments.length == 1 && (arguments[0] instanceof Cal3D.CalQuaternion)) {
		var other = arguments[0];
		this.x = other.x;
		this.y = other.y;
		this.z = other.z;
		this.w = other.w;
	}
	else if(arguments.length == 4) {
		this.x = arguments[0];
		this.y = arguments[1];
		this.z = arguments[2];
		this.w = arguments[3];
	}

	return this;
};

/**
	Multiply another quaternion to this quaternion.
*/
Cal3D.CalQuaternion.prototype.multQuaternionLocal = function(q) {
	var qx = this.x;
	var qy = this.y;
	var qz = this.z;
	var qw = this.w;
	
	this.x = qw * q.x + qx * q.w + qy * q.z - qz * q.y;
	this.y = qw * q.y - qx * q.z + qy * q.w + qz * q.x;
	this.z = qw * q.z + qx * q.y - qy * q.x + qz * q.w;
	this.w = qw * q.w - qx * q.x - qy * q.y - qz * q.z;

	return this;
};

/**
	Multiply a vector to this quaternion.
*/
Cal3D.CalQuaternion.prototype.multVectorLocal = function(v) {
	var qx = this.x;
	var qy = this.y;
	var qz = this.z;
	var qw = this.w;
	
	this.x = qw * v.x            + qy * v.z - qz * v.y;
	this.y = qw * v.y - qx * v.z            + qz * v.x;
	this.z = qw * v.z + qx * v.y - qy * v.x;
	this.w =          - qx * v.x - qy * v.y - qz * v.z;

	return this;
};

/**
	See if this quaternion equals to another quaternion.
*/
Cal3D.CalQuaternion.prototype.equalTo = function(q) {
	return  this.x == q.x && 
			this.y == q.y && 
			this.z == q.z && 
			this.w == q.w;
};

/**
	Interpolate this quaternion to another quaternion by a given factor.
*/
Cal3D.CalQuaternion.prototype.blend = function(d, q) {
	var norm = this.x * q.x + this.y * q.y + this.z * q.z + this.w * q.w;
	
	var bFlip = false;	
	if(norm < 0)
	{
		norm = -norm;
		bFlip = true;
	}
	
	var inv_d;
	if(1 - norm < 1e-6)
	{
		inv_d = 1 - d;
	}
	else
	{
		var theta = Math.acos(norm);
		var s = 1 / Math.sin(theta);
		
		inv_d = Math.sin((1 - d) * theta) * s;
		d = Math.sin(d * theta) * s;
	}
	
	if(bFlip)
	{
		d = -d;
	}
	
	this.x = inv_d * this.x + d * q.x;
	this.y = inv_d * this.y + d * q.y;
	this.z = inv_d * this.z + d * q.z;
	this.w = inv_d * this.w + d * q.w;
};

/**
	Clear the quaternion.
*/
Cal3D.CalQuaternion.prototype.clear = function() {
	this.x = 0;
	this.y = 0;
	this.z = 0;
	this.w = 1;
};

/**
	Conjugate the quaternion.
*/
Cal3D.CalQuaternion.prototype.conjugate = function() {
	this.x = -this.x;
	this.y = -this.y;
	this.z = -this.z;
};

/**
	Invert the quaternion.
*/
Cal3D.CalQuaternion.prototype.invert = function() {
	this.conjugate();

	var norm = (this.x*this.x) + (this.y*this.y) + (this.z*this.z) + (this.w*this.w);	
	if (norm == 0) 
		return;
	
	var inv_norm = 1 / norm;
	this.x *= inv_norm;
	this.y *= inv_norm;
	this.z *= inv_norm;
	this.w *= inv_norm;
};

/**
	Multiply two quaternions and return a new quaternion that holds the result.
*/
Cal3D.quaternionMult = function(q0, q1) {
	return new Cal3D.CalQuaternion(
		q1.w * q0.x + q1.x * q0.w + q1.y * q0.z - q1.z * q0.y,
		q1.w * q0.y - q1.x * q0.z + q1.y * q0.w + q1.z * q0.x,
		q1.w * q0.z + q1.x * q0.y - q1.y * q0.x + q1.z * q0.w,
		q1.w * q0.w - q1.x * q0.x - q1.y * q0.y - q1.z * q0.z
	);
};

/**
	Calculate the shortest arc quaternion that will rotate one vector to another and return a quaternion that holds the result.
*/
Cal3D.shortestArc = function(from, to) {
	var cp = Cal3D.vectorCross(from, to); 
	var dp = Cal3D.vectorDot(from, to);
	
	// we will use this equation twice
	dp = Math.sqrt( 2*(dp+1) ); 
	
	// get the x, y, z components
	cp.divScalarLocal(dp);
	
	// return with the w component (Note that w is inverted because Cal3D has left-handed rotations)
	return new Cal3D.CalQuaternion( cp.x, cp.y, cp.z, -dp/2 );
};



/**
	@class CalVector
*/
Cal3D.CalVector = function() {
	this.x = 0;
	this.y = 0;
	this.z = 0;

	this.assign.apply(this, arguments);
};

/**
	Directly assign components of the vector, or copy componebts from another vector.
*/
Cal3D.CalVector.prototype.assign = function() {
	if(arguments.length == 1 && (arguments[0] instanceof Cal3D.CalVector)) {
		var other = arguments[0];
		this.x = other.x;
		this.y = other.y;
		this.z = other.z;
	}
	else if(arguments.length == 3) {
		this.x = arguments[0];
		this.y = arguments[1];
		this.z = arguments[2];
	}

	return this;
};

/**
	Add another vector to this vector.
*/
Cal3D.CalVector.prototype.addLocal = function(v) {
	this.x += v.x;
	this.y += v.y;
	this.z += v.z;

	return this;
};

/**
	Subtract another vector from this vector.
*/
Cal3D.CalVector.prototype.subLocal = function(v) {
	this.x -= v.x;
	this.y -= v.y;
	this.z -= v.z;

	return this;
};

/**
	Scale the vector by a given factor.
*/
Cal3D.CalVector.prototype.multScalarLocal = function(factor) {
	this.x *= factor;
	this.y *= factor;
	this.z *= factor;

	return this;
};

/**
	Transform the vector by a quaternion.
*/
Cal3D.CalVector.prototype.multQuaternionLocal = function(q) {
	var temp = new Cal3D.CalQuaternion(-q.x, -q.y, -q.z, q.w);
	temp.multVectorLocal(this).multQuaternionLocal(q);

	this.x = temp.x;
	this.y = temp.y;
	this.z = temp.z;

	return this;
};

/**
	Transform the vector by a matrix.
*/
Cal3D.CalVector.prototype.multMatrixLocal = function(m) {
	var ox = this.x;
	var oy = this.y;
	var oz = this.z;
	this.x = m.dxdx*ox + m.dxdy*oy + m.dxdz*oz;
	this.y = m.dydx*ox + m.dydy*oy + m.dydz*oz;
	this.z = m.dzdx*ox + m.dzdy*oy + m.dzdz*oz;

	return this;
};

/**
	Divide the vector by a given factor.
*/
Cal3D.CalVector.prototype.divScalarLocal = function(factor) {
	this.x /= factor;
	this.y /= factor;
	this.z /= factor;

	return this;
};

/**
	See if this vector equals another vector.
*/
Cal3D.CalVector.prototype.equalTo = function(v) {
	return  this.x == v.x && 
			this.y == v.y && 
			this.z == v.z;
};

/**
	Interpolate this vector to another vector by a given factor.
*/
Cal3D.CalVector.prototype.blend = function(d, v) {
	this.x += d * (v.x - this.x);
	this.y += d * (v.y - this.y);
	this.z += d * (v.z - this.z);
};

/**
	Clear the vector.
*/
Cal3D.CalVector.prototype.clear = function() {
	this.x = 0;
	this.y = 0;
	this.z = 0;
};

/**
	Get the length of the vector.
*/
Cal3D.CalVector.prototype.length = function() {
	return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
};

/**
	Normalize the vector and renturn its former length.
*/
Cal3D.CalVector.prototype.normalize = function() {
	var len = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
	this.x /= len;
	this.y /= len;
	this.z /= len;

	return len;
};

/**
	Add two vectors and return a new vector that holds the result.
*/
Cal3D.vectorAdd = function(v0, v1) {
	return new Cal3D.CalVector(v0.x + v1.x, v0.y + v1.y, v0.z + v1.z);
};

/**
	Subtract two vectors and return a new vector that holds the result.
*/
Cal3D.vectorSub = function(v0, v1) {
	return new Cal3D.CalVector(v0.x - v1.x, v0.y - v1.y, v0.z - v1.z);
};

/**
	Multiply a vector and a scalar and return a new vector that holds the result.
*/
Cal3D.vectorScalarMult = function(v, d) {
	return new Cal3D.CalVector(v.x * d, v.y * d, v.z * d);
};

/**
	Divide a vector by a scalar and return a new vector that holds the result.
*/
Cal3D.vectorScalarDiv = function(v, d) {
	return new Cal3D.CalVector(v.x / d, v.y / d, v.z / d);
};

/**
	Calculate dot product of two vectors and return a new vector that holds the result.
*/
Cal3D.vectorDot = function(v0, v1) {
	return v0.x * v1.x + v0.y * v1.y + v0.z * v1.z;
};

/**
	Calculate cross product of two vectors and return a new vector that holds the result.
*/
Cal3D.vectorCross = function(v0, v1) {
	return new Cal3D.CalVector(v0.y * v1.z - v0.z * v1.y, v0.z * v1.x - v0.x * v1.z, v0.x * v1.y - v0.y * v1.x);
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalPlane
	@private
*/
Cal3D.CalPlane = function() {
	this.a = 0;
	this.b = 0;
	this.c = 0;
	this.d = 0;
};

Cal3D.CalPlane.prototype.eval = function(p) {
	return p.x * this.a + p.y * this.b + p.z * this.c + this.d;
};

Cal3D.CalPlane.prototype.setPosition = function(p) {
	this.d = -p.x * this.a - p.y * this.b - p.z * this.c;
};

Cal3D.CalPlane.prototype.setNormal = function(n) {
	 this.a = n.x;
	 this.b = n.y;
	 this.c = n.z;
	 this.d = -1e32;
};

Cal3D.CalPlane.prototype.dist = function(p) {
	return Math.abs( (p.x * this.a + p.y * this.b + p.z * this.c + this.d) / Math.sqrt(this.a * this.a + this.b * this.b + this.c * this.c) );
};



/**
	@class CalBoundingBox
*/
Cal3D.CalBoundingBox = function() {
	this.plane = [  
		new Cal3D.CalPlane, 
		new Cal3D.CalPlane, 
		new Cal3D.CalPlane, 
		new Cal3D.CalPlane, 
		new Cal3D.CalPlane, 
		new Cal3D.CalPlane
	];
};

/**
	Calculate the corner points of the bounding box.<br />
	This function computes the 8 corner points of the bounding box.
	@param {Array} points An array of 8 vectors to take the result. If null, the function will allocate one.
	@returns {Array} An array that holds 8 vectors as the result.
*/
Cal3D.CalBoundingBox.prototype.computePoints = function(points) {
	if(!points || !(points instanceof Array) || points.length < 8)
		points = new Array(8);

	var m = new Cal3D.CalMatrix;

	var plane = this.plane;	 
	var p = 0;
	for(var i=0; i<2; i++) {
		for(var j=2; j<4; j++) {
			for(var k=4; k<6; k++) {
				var x,y,z;

				m.dxdx = plane[i].a; m.dxdy = plane[i].b; m.dxdz = plane[i].c;        
				m.dydx = plane[j].a; m.dydy = plane[j].b; m.dydz = plane[j].c;        
				m.dzdx = plane[k].a; m.dzdy = plane[k].b; m.dzdz = plane[k].c;

				var det = m.det();
			   
				if(det != 0) {
					m.dxdx = -plane[i].d; m.dxdy = plane[i].b; m.dxdz = plane[i].c;        
					m.dydx = -plane[j].d; m.dydy = plane[j].b; m.dydz = plane[j].c;        
					m.dzdx = -plane[k].d; m.dzdy = plane[k].b; m.dzdz = plane[k].c;

					x = m.det() / det;

					m.dxdx = plane[i].a; m.dxdy = -plane[i].d; m.dxdz = plane[i].c;        
					m.dydx = plane[j].a; m.dydy = -plane[j].d; m.dydz = plane[j].c;        
					m.dzdx = plane[k].a; m.dzdy = -plane[k].d; m.dzdz = plane[k].c;

					y = m.det() / det;

					m.dxdx = plane[i].a; m.dxdy = plane[i].b; m.dxdz = -plane[i].d;        
					m.dydx = plane[j].a; m.dydy = plane[j].b; m.dydz = -plane[j].d;        
					m.dzdx = plane[k].a; m.dzdy = plane[k].b; m.dzdz = -plane[k].d;

					z = m.det() / det;

					if(points[p])
						points[p].assign(x, y, z);
					else
						points[p] = new Cal3D.CalVector(x, y, z);
				}
				else {
					if(points[p])
						points[p].assign(0, 0, 0);
					else
						points[p] = new Cal3D.CalVector(0, 0, 0);
				}

				p++;
			}
		}
	}

	return points;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalCoreMaterial
*/
Cal3D.CalCoreMaterial = function() {
	this.m_ambientColor = new Cal3D.CalCoreMaterial.Color;
	this.m_diffuseColor = new Cal3D.CalCoreMaterial.Color;
	this.m_specularColor = new Cal3D.CalCoreMaterial.Color;
	this.m_shininess = 0;
	this.m_vectorMap = [];
	this.m_userData = null;
	this.m_name = '';
	this.m_filename = '';
};

/**
	Get the ambient color.<br />
	This function returns the ambient color of the core material instance.
	@returns {Cal3D.CalCoreMaterial.Color} The ambient color.
*/
Cal3D.CalCoreMaterial.prototype.getAmbientColor = function() {
	return this.m_ambientColor;
};

/**
	Get the diffuse color.<br />
	This function returns the diffuse color of the core material instance.
	@returns {Cal3D.CalCoreMaterial.Color} The diffuse color.
*/
Cal3D.CalCoreMaterial.prototype.getDiffuseColor = function() {
	return this.m_diffuseColor;
};

/**
	Get the number of texture maps.<br />
	This function returns the number of texture mapss in the core material instance.
	@returns {number} The number of texture maps.
*/
Cal3D.CalCoreMaterial.prototype.getMapCount = function() {
	return this.m_vectorMap.length;
};

/**
	Get a specified map texture filename.<br />
	This function returns the texture filename for a specified map ID of the core material instance.
	@param {number} mapId The ID of the texture map.
	@returns {string} The filename of the map texture. An empty string will be returned if the map does not exist.
*/
Cal3D.CalCoreMaterial.prototype.getMapFilename = function(mapId) {
	if(mapId < 0 || mapId >= this.m_vectorMap.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'corematerial.js');
		return '';
	}

	var map = this.m_vectorMap[mapId];
	if(!map)
		return '';

	return map.filename;
};

/**
	Get user data of a specified textue map.<br />
	This function returns the user data stored in the specified map of the core material instance.
	@param {number} mapId The ID of the texture map.
	@returns {object} The user data stored in the specified map; null if the map does not exist.
*/
Cal3D.CalCoreMaterial.prototype.getMapUserData = function(mapId) {
	if(mapId < 0 || mapId >= this.m_vectorMap.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'corematerial.js');
		return null;
	}

	var map = this.m_vectorMap[mapId];
	if(!map)
		return null;

	return map.userData;
};

/**
	Get the shininess factor.<br />
	This function returns the shininess factor of the core material instance.
	@returns {number} The shininess factor.
*/
Cal3D.CalCoreMaterial.prototype.getShininess = function() {
	return this.m_shininess;
};

/**
	Get the specular color.<br />
	This function returns the specular color of the core material instance.
	@returns {Cal3D.CalCoreMaterial.Color} The specular color.
*/
Cal3D.CalCoreMaterial.prototype.getSpecularColor = function() {
	return this.m_specularColor;
};

/**
	Get the user data stored in this core material.<br />
	This function returns the user data stored in the core material instance.
	@returns {object} The user data stored in the core material.
*/
Cal3D.CalCoreMaterial.prototype.getUserData = function() {
	return this.m_userData;
};

/**
	Get the map array.<br />
	This function returns the array that contains all maps of the core material instance.
	@returns {array} The map array.
*/
Cal3D.CalCoreMaterial.prototype.getVectorMap = function() {
	return this.m_vectorMap;
};

/**
	Allocate space for the maps<br />
	This function reserves space for the maps of the core material instance.
	@param {number} mapCount The number of maps that this core material should holds.
	@returns {boolean} true, if succeeded; false if failed.
*/
Cal3D.CalCoreMaterial.prototype.reserve = function(mapCount) {
	this.m_vectorMap = new Array(mapCount);
	return true;
};

/**
	Set the ambient color.<br />
	This function sets the ambient color of the core material instance.
	@param {Cal3D.CalCoreMaterial.Color} ambientColor The ambient color to set.
*/
Cal3D.CalCoreMaterial.prototype.setAmbientColor = function(ambientColor) {
	this.m_ambientColor.red = ambientColor.red;
	this.m_ambientColor.green = ambientColor.green;
	this.m_ambientColor.blue = ambientColor.blue;
	this.m_ambientColor.alpha = ambientColor.alpha;
};

/**
	Set the diffuse color.<br />
	This function sets the diffuse color of the core material instance.
	@param {Cal3D.CalCoreMaterial.Color} diffuseColor The diffuse color to set.
*/
Cal3D.CalCoreMaterial.prototype.setDiffuseColor = function(diffuseColor) {
	this.m_diffuseColor.red = diffuseColor.red;
	this.m_diffuseColor.green = diffuseColor.green;
	this.m_diffuseColor.blue = diffuseColor.blue;
	this.m_diffuseColor.alpha = diffuseColor.alpha;
};

/**
	Set a specified texture map.<br />
	This function sets a specified map in the core material instance.
	@param {number} mapId The ID of the map.
	@param {Cal3D.CalCoreMaterial.Map} map The map to be set.
	@returns {boolean} true if succeeded; false if the map does not exist.
*/
Cal3D.CalCoreMaterial.prototype.setMap = function(mapId, map) {
	if(mapId < 0 || mapId >= this.m_vectorMap.length)
		return false;

	this.m_vectorMap[mapId] = map;
	return true;
};

/**
	Store specified map user data.<br />
	This function stores user data in a specified map of the core material instance.
	@param {number} mapId The ID of the map.
	@param {object} userData The map to be set.
	@returns {boolean} true if succeeded; false if the map does not exist.
*/
Cal3D.CalCoreMaterial.prototype.setMapUserData = function(mapId, userData) {
	if(mapId < 0 || mapId >= this.m_vectorMap.length)
		return false;

	var map = this.m_vectorMap[mapId];
	if(!map)
		return false;

	map.userData = userData;
	return true;
};

/**
	Set the shininess factor.<br />
	This function sets the shininess factor of the core material instance.
	@param {number} shininess The shininess factor to be set.
*/
Cal3D.CalCoreMaterial.prototype.setShininess = function(shininess) {
	this.m_shininess = shininess;
};

/**
	Set the specular color.<br />
	This function sets the specular color of the core material instance.
	@param {Cal3D.CalCoreMaterial.Color} ambientColor The specular color to be set.
*/
Cal3D.CalCoreMaterial.prototype.setSpecularColor = function(specularColor) {
	this.m_specularColor.red = specularColor.red;
	this.m_specularColor.green = specularColor.green;
	this.m_specularColor.blue = specularColor.blue;
	this.m_specularColor.alpha = specularColor.alpha;
};

/**
	Set the name of the file in which the core material is stored, if any.<br />
	@param {string} filename The file name.
*/
Cal3D.CalCoreMaterial.prototype.setFilename = function(filename) {
	this.m_filename = filename;
};

/**
	Get the name of the file in which the core material is stored, if any.<br />
	@returns {string} The file name. An empty string will be returned if the material is not stored in a file.
*/
Cal3D.CalCoreMaterial.prototype.getFilename = function() {
	return this.m_filename;
};

/**
	Set the symbolic name of the core material.<br />
	@param {string} name The symbolic name to be set.
*/
Cal3D.CalCoreMaterial.prototype.setName = function(name) {
	this.m_name = name;
};

/**
	Get the symbolic name of the core material.<br />
	@returns {string} The symbolic name of the material. An empty string will be returned if the material is not associated to a symbolic name yet.
*/
Cal3D.CalCoreMaterial.prototype.getName = function() {
	return this.m_name;
};

/**
	Store user data.<br />
	This function stores user data in the core material instance.
	@param {object} userData The use data to be stored in this material.
*/
Cal3D.CalCoreMaterial.prototype.setUserData = function(userData) {
	this.m_userData = userData;
};


/**
	@class Color

	The core material color structure.
*/
Cal3D.CalCoreMaterial.Color = function() {
	this.red = 0;
	this.green = 0;
	this.blue = 0;
	this.alpha = 0;
};



/**
	@class Map

	The core material map structure.
*/
Cal3D.CalCoreMaterial.Map = function() {
	this.filename = '';
	this.userData = null;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalCoreSubmesh
*/
Cal3D.CalCoreSubmesh = function() {
	this.m_vectorVertex = [];
	this.m_vectorTangentsEnabled = [];
	this.m_vectorvectorTangentSpace = [];
	this.m_vectorvectorTextureCoordinate = [];
	this.m_vectorPhysicalProperty = [];
	this.m_vectorFace = [];
	this.m_vectorSpring = [];
	this.m_vectorCoreSubMorphTarget = [];
	this.m_coreMaterialThreadId = 0;
	this.m_lodCount = 0;
};

/**
	Get the ID of the core material thread.<br />
	This function returns the ID of the core material thread of this core submesh instance.
	@returns {number} The ID of the core material thread.
*/
Cal3D.CalCoreSubmesh.prototype.getCoreMaterialThreadId = function() {
	return this.m_coreMaterialThreadId;
};

/**
	Get the number of faces.<br />
	This function returns the number of faces in the core submesh instance.
	@returns {number} The number of faces.
*/
Cal3D.CalCoreSubmesh.prototype.getFaceCount = function() {
	return this.m_vectorFace.length;
};

/**
	Get the number of LOD steps.<br />
	This function returns the number of LOD steps in the core submesh instance.
	@returns {number} The number of LOD steps.
*/
Cal3D.CalCoreSubmesh.prototype.getLodCount = function() {
	return this.m_lodCount;
};

/**
	Get the number of springs.<br />
	This function returns the number of springs in the core submesh instance.
	@returns {number} The number of springs.
*/
Cal3D.CalCoreSubmesh.prototype.getSpringCount = function() {
	return this.m_vectorSpring.length;
};

/**
	Get the face list.<br />
	This function returns the list that contains all faces of the core submesh instance.
	@returns {Array} The face list.
*/
Cal3D.CalCoreSubmesh.prototype.getVectorFace = function() {
	return this.m_vectorFace;
};

/**
	Get the physical property list.<br />
	This function returns the list that contains all physical properties of the core submesh instance.
	@returns {Array} The physical property list.
*/
Cal3D.CalCoreSubmesh.prototype.getVectorPhysicalProperty = function() {
	return this.m_vectorPhysicalProperty;
};

/**
	Get the spring list.<br />
	This function returns the list that contains all springs of the core submesh instance.
	@returns {Array} The spring list.
*/
Cal3D.CalCoreSubmesh.prototype.getVectorSpring = function() {
	return this.m_vectorSpring;
};

/**
	Get list of the tangent space lists.<br />
	This function returns the list that contains all tangent space bases of the core submesh instance. This list contains 
	another list because there can be more than one texture map for each vertex.
	@returns {Array} The list of the tangent space lists
*/
Cal3D.CalCoreSubmesh.prototype.getVectorVectorTangentSpace = function() {
	return this.m_vectorvectorTangentSpace;
};

/**
	Get list of the texture coordinate lists.<br />
	This function returns the list that contains all texture coordinate vectors of the core submesh instance. This list contains 
	another list because there can be more than one texture map for each vertex.
	@returns {Array} The list of the texture coordinate lists.
*/
Cal3D.CalCoreSubmesh.prototype.getVectorVectorTextureCoordinate = function() {
	return this.m_vectorvectorTextureCoordinate;
};

/**
	Get the vertex list.<br />
	This function returns the vector that contains all vertices of the core submesh instance.
	@returns {Array} The vertex list.
*/
Cal3D.CalCoreSubmesh.prototype.getVectorVertex = function() {
	return this.m_vectorVertex;
};

/**
	Get the number of vertices.<br />
	This function returns the number of vertices in the core submesh instance.
	@returns {number} The number of vertices.
*/
Cal3D.CalCoreSubmesh.prototype.getVertexCount = function() {
	return this.m_vectorVertex.length;
};

/**
	See if tangent lists are enabled.<br />
	This function returns true if the core submesh contains tangent lists.
	@param {number} mapId The ID of the texture map.
	@returns {boolean} true if tangent lists are enabled for the given map; false if not.
*/
Cal3D.CalCoreSubmesh.prototype.isTangentsEnabled = function(mapId) {
	if(mapId < 0 || mapId >= this.m_vectorTangentsEnabled.length)
		return false;

	return this.m_vectorTangentsEnabled[mapId];
};

/**
	Enable (and calculate) or disable the storage of tangent spaces.<br />
	@param {number} mapId The ID of the texture map.
	@param {boolean} enabled Whether the storage of tangent spaces should be enabled.
	@returns {boolean} true if succeeded; false if failed.
*/
Cal3D.CalCoreSubmesh.prototype.enableTangents = function(mapId, enabled) {
	if(mapId < 0 || mapId >= this.m_vectorTangentsEnabled.length)
		return false;

	this.m_vectorTangentsEnabled[mapId] = enabled;

	if(!enabled) {
		this.m_vectorvectorTangentSpace[mapId] = [];
		return true;
	}

	this.m_vectorvectorTangentSpace[mapId] = new Array(this.m_vectorVertex.length);

	for(var tangentId=0; tangentId<this.m_vectorvectorTangentSpace[mapId].length; tangentId++) {
		this.m_vectorvectorTangentSpace[mapId][tangentId] = new Cal3D.CalCoreSubmesh.TangentSpace;
	}

	for(var faceId=0; faceId<this.m_vectorFace.length; faceId++) {
		this.UpdateTangentVector(this.m_vectorFace[faceId].vertexId[0], this.m_vectorFace[faceId].vertexId[1], this.m_vectorFace[faceId].vertexId[2], mapId);
		this.UpdateTangentVector(this.m_vectorFace[faceId].vertexId[1], this.m_vectorFace[faceId].vertexId[2], this.m_vectorFace[faceId].vertexId[0], mapId);
		this.UpdateTangentVector(this.m_vectorFace[faceId].vertexId[2], this.m_vectorFace[faceId].vertexId[0], this.m_vectorFace[faceId].vertexId[1], mapId);
	}

	for(var tangentId=0; tangentId<this.m_vectorvectorTangentSpace[mapId].length; tangentId++) {
		this.m_vectorvectorTangentSpace[mapId][tangentId].tangent.normalize();
	}

	return true;
};

/**
	Allocate space for the vertices, faces and texture coordinates and springs.<br />
	This function reserves space for the vertices, faces, texture coordinates and springs of the core submesh instance.
	@param {number} vertexCount The number of vertices that this core submesh should hold.
	@param {number} textureCoordinateCount The number of texture coordinates that this core submesh should hold.
	@param {number} faceCount The number of faces that this core submesh should hold.
	@param {number} springCount The number of springs that this core submesh should hold.
	@returns {boolean} true if succeeded; false if failed.
*/
Cal3D.CalCoreSubmesh.prototype.reserve = function(vertexCount, textureCoordinateCount, faceCount, springCount) {
	/*
		reserve the space needed in all the vectors
	*/

	this.m_vectorVertex = new Array(vertexCount);
	this.m_vectorTangentsEnabled = new Array(textureCoordinateCount);
	this.m_vectorvectorTangentSpace = new Array(textureCoordinateCount);
	this.m_vectorvectorTextureCoordinate = new Array(textureCoordinateCount);


	for(var textureCoordinateId=0; textureCoordinateId<textureCoordinateCount; textureCoordinateId++) {
		this.m_vectorvectorTextureCoordinate[textureCoordinateId] = new Array(vertexCount);

		if (this.m_vectorTangentsEnabled[textureCoordinateId])
			this.m_vectorvectorTangentSpace[textureCoordinateId] = new Array(vertexCount);
		else
			this.m_vectorvectorTangentSpace[textureCoordinateId] = [];
	}

	this.m_vectorFace = new Array(faceCount);
	this.m_vectorSpring = new Array(springCount);

	// reserve the space for the physical properties if we have springs in the core submesh instance
	if(springCount > 0)
		this.m_vectorPhysicalProperty = new Array(vertexCount);

	return true;
};

/**
	Set the ID of the core material thread.<br />
	This function sets the ID of the core material thread of the core submesh instance.
	@param {number} coreMaterialThreadId The ID of the core material thread to be set.
*/
Cal3D.CalCoreSubmesh.prototype.setCoreMaterialThreadId = function(coreMaterialThreadId) {
	this.m_coreMaterialThreadId = coreMaterialThreadId;
};

/**
	Set a specified face.<br />
	This function sets a specified face in the core submesh instance.
	@param {number} faceId The ID of the face.
	@param {Cal3D.CalCoreSubmesh.Face} face The face to set to the core submesh.
	@returns {boolean} true if succeeded; false if the face does not exist.
*/
Cal3D.CalCoreSubmesh.prototype.setFace = function(faceId, face) {
	if(faceId < 0 || faceId >= this.m_vectorFace.length)
		return false;

	this.m_vectorFace[faceId] = face;
	return true;
};

/**
	Set the number of LOD steps.<br />
	This function sets the number of LOD steps of the core submesh instance.
	@param {number} lodCount The number of LOD steps to be set.
*/
Cal3D.CalCoreSubmesh.prototype.setLodCount = function(lodCount) {
	this.m_lodCount = lodCount;
};

/**
	Set a specified physical property.<br />
	This function sets a specified physical property in the core submesh instance.
	@param {number} vertexId The ID of the vertex.
	@param {Cal3D.CalCoreSubmesh.PhysicalProperty} physicalProperty The physical property to be set.
	@returns {boolean} true if succeeded; false if the physical property does not exist.
*/
Cal3D.CalCoreSubmesh.prototype.setPhysicalProperty = function(vertexId, physicalProperty) {
	if(vertexId < 0 || vertexId >= this.m_vectorPhysicalProperty.length)
		return false;

	this.m_vectorPhysicalProperty[vertexId] = physicalProperty;
	return true;
};

/**
	Set a specified spring.<br />
	This function sets a specified spring in the core submesh instance.
	@param {number} springId The ID of the spring.
	@param {Cal3D.CalCoreSubmesh.Spring} spring The spring to be set.
	@returns {boolean} true if succeeded; false if the spring does not exist.
*/
Cal3D.CalCoreSubmesh.prototype.setSpring = function(springId, spring) {
	if(springId < 0 || springId >= this.m_vectorSpring.length)
		return false;

	this.m_vectorSpring[springId] = spring;
	return true;
};

/**
	Set the tangent vector associated with a specified texture coordinate pair.<br />
	This function sets the tangent vector associated with a specified texture coordinate pair in the core submesh instance.
	@param {number} vertexId The ID of the vertex.
	@param {number} textureCoordinateId The ID of the texture coordinate channel.
	@param {Cal3D.CalVector} tangent The tangent vector to be stored.
	@param {number} crossFactor The cross-product factor to be stored.
	@returns {boolean} true if succeeded; false if any error happened.
*/
Cal3D.CalCoreSubmesh.prototype.setTangentSpace = function(vertexId, textureCoordinateId, tangent, crossFactor) {
	if(vertexId < 0 || vertexId >= this.m_vectorVertex.length) 
		return false;
	if(textureCoordinateId < 0 || textureCoordinateId >= this.m_vectorvectorTextureCoordinate.length) 
		return false;
	if(!this.m_vectorTangentsEnabled[textureCoordinateId]) 
		return false;

	//this.m_vectorvectorTangentSpace[textureCoordinateId][vertexId].tangent = tangent;
	this.m_vectorvectorTangentSpace[textureCoordinateId][vertexId].tangent.assign(tangent);
	this.m_vectorvectorTangentSpace[textureCoordinateId][vertexId].crossFactor = crossFactor;

	return true;
};

/**
	Set a specified texture coordinate.<br />
	This function sets a specified texture coordinate in the core submesh instance.
	@param {number} vertexId The ID of the vertex.
	@param {number} textureCoordinateId The ID of the texture coordinate.
	@param {Cal3D.CalCoreSubmesh.TextureCoordinate} The texture coordinate to set.
	@returns {boolean} true if succeeded; false if the texture coordinate does not exist.
*/
Cal3D.CalCoreSubmesh.prototype.setTextureCoordinate = function(vertexId, textureCoordinateId, textureCoordinate) {
	if(textureCoordinateId < 0 || textureCoordinateId >= this.m_vectorvectorTextureCoordinate.length)
		return false;
	if(vertexId < 0 || vertexId >= this.m_vectorvectorTextureCoordinate[textureCoordinateId].length)
		return false;

	this.m_vectorvectorTextureCoordinate[textureCoordinateId][vertexId] = textureCoordinate;
	return true;
};

/**
	Set a vertex.<br />
	This function sets a specified vertex in the core submesh instance.
	@param {number} vertexId The ID of the vertex.
	@param {Cal3D.CalCoreSubmesh.Vertex} The vertex to set.
	@returns {boolean} true if succeeded; false if the vertex does not exist.
*/
Cal3D.CalCoreSubmesh.prototype.setVertex = function(vertexId, vertex) {
	if(vertexId < 0 || vertexId >= this.m_vectorVertex.length)
		return false;

	this.m_vectorVertex[vertexId] = vertex;
	return true;
};

/**
	Add a core sub morph target.<br />
	This function adds a core sub morph target to the core sub mesh instance.
	@param {Cal3D.CalCoreSubMorphTarget} coreSubMorphTarget The core sub morph target to be added.
	@returns {number} ID of the added core sub morph target.
*/
Cal3D.CalCoreSubmesh.prototype.addCoreSubMorphTarget = function(coreSubMorphTarget) {
	var subMorphTargetId = this.m_vectorCoreSubMorphTarget.length;
	this.m_vectorCoreSubMorphTarget.push(coreSubMorphTarget);
	return subMorphTargetId;
};

/**
	Get a core sub morph target.<br />
	This function returns the core sub morph target with the given ID.
	@param {number} subMorphTargetId The ID of the core sub morph target.
	@returns {Cal3D.CalCoreSubMorphTarget} The core sub morph target.
*/
Cal3D.CalCoreSubmesh.prototype.getCoreSubMorphTarget = function(subMorphTargetId) {
	if(subMorphTargetId < 0 || subMorphTargetId >= this.m_vectorCoreSubMorphTarget.length)
		return null;

	return this.m_vectorCoreSubMorphTarget[subMorphTargetId];
};

/**
	Get the number of core sub morph targets.<br />
	This function returns the number of core sub morph targets in the core sub mesh instance.
	@returns {number} The number of core sub morph targets. 
*/
Cal3D.CalCoreSubmesh.prototype.getCoreSubMorphTargetCount = function() {
	return this.m_vectorCoreSubMorphTarget.length;
};

/**
	Get the core sub morph target list.<br />
	This function returns the list that contains all core sub morph target of the core submesh instance.
	@returns {Array} The core sub morph target list.
*/
Cal3D.CalCoreSubmesh.prototype.getVectorCoreSubMorphTarget = function() {
	return this.m_vectorCoreSubMorphTarget;
};

/**
	Scale the core submesh.<br />
	This function rescale all the data that are in the core submesh instance.
	@param {number} factor The scale factor.
*/
Cal3D.CalCoreSubmesh.prototype.scale = function(factor) {
	// rescale all vertices
	for(var vertexId=0; vertexId<this.m_vectorVertex.length; vertexId++) {
		this.m_vectorVertex[vertexId].position.multScalarLocal(factor);
	}

	if(this.m_vectorSpring.length > 0) {
		// There is a problem when we resize and that there is
		// a spring system, I was unable to solve this
		// problem, so I disable the spring system
		// if the scale are too big
		if( Math.abs(factor - 1) > 0.1) {
			this.m_vectorSpring = [];
			this.m_vectorPhysicalProperty = [];
		}
	}
};

/**
	@private
*/
Cal3D.CalCoreSubmesh.prototype.UpdateTangentVector = function(v0, v1, v2, mapId) {
	var vvtx = this.getVectorVertex();
	var vtex = this.m_vectorvectorTextureCoordinate[mapId];

	// Step 1. Compute the approximate tangent vector.
	var du1 = vtex[v1].u - vtex[v0].u;
	var dv1 = vtex[v1].v - vtex[v0].v;
	var du2 = vtex[v2].u - vtex[v0].u;
	var dv2 = vtex[v2].v - vtex[v0].v;

	var prod1 = (du1*dv2-dv1*du2);
	var prod2 = (du2*dv1-dv2*du1);
	if (Math.abs(prod1) < 0.000001 || Math.abs(prod2) < 0.000001)
		return;

	var x = dv2 / prod1;
	var y = dv1 / prod2;

	var vec1 = Cal3D.vectorSub(vvtx[v1].position, vvtx[v0].position);
	var vec2 = Cal3D.vectorSub(vvtx[v2].position, vvtx[v0].position);
	var tangent = vec1.multScalarLocal(x).addLocal(vec2.multScalarLocal(y));

	// Step 2. Orthonormalize the tangent.
	var component = Cal3D.vectorDot(tangent, vvtx[v0].normal);
	tangent.subLocal(Cal3D.vectorScalarMult(vvtx[v0].normal, component));
	tangent.normalize();

	// Step 3: Add the estimated tangent to the overall estimate for the vertex.
	this.m_vectorvectorTangentSpace[mapId][v0].tangent.addLocal(tangent);
};



/**
	@class TextureCoordinate

	The texture coordinate structure for the CalCoreSubmesh class.
*/
Cal3D.CalCoreSubmesh.TextureCoordinate = function() {
	this.u = 0;
	this.v = 0;
};



/**
	@class TangentSpace

	The tangent space structure for the CalCoreSubmesh class.
*/
Cal3D.CalCoreSubmesh.TangentSpace = function() {
	this.tangent = new Cal3D.CalVector(0, 0, 0);
	this.crossFactor = 1;	// To get the binormal, use ((N x T) * crossFactor)
};



/**
	@class Influence

	The influence structure for the CalCoreSubmesh class.
*/
Cal3D.CalCoreSubmesh.Influence = function() {
	this.boneId = 0;
	this.weight = 0;
};



/**
	@class PhysicalProperty

	The physical property structure for the CalCoreSubmesh class.
*/
Cal3D.CalCoreSubmesh.PhysicalProperty = function() {
	this.weight = 0;
};



/**
	@class Vertex

	The vertex structure for the CalCoreSubmesh class.
*/
Cal3D.CalCoreSubmesh.Vertex = function() {
	this.position = new Cal3D.CalVector;
	this.normal = new Cal3D.CalVector;
	this.vectorInfluence = [];
	this.collapseId = 0;
	this.faceCollapseCount = 0;
};



/**
	@class Face 

	The face structure for the CalCoreSubmesh class.
*/
Cal3D.CalCoreSubmesh.Face = function() {
	this.vertexId = [0, 0, 0];
};



/**
	@class Spring

	The spring structure for the CalCoreSubmesh class.
*/
Cal3D.CalCoreSubmesh.Spring = function() {
	this.vertexId = [0, 0];
	this.springCoefficient = 0;
	this.idleLength = 0;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalCoreMesh
*/
Cal3D.CalCoreMesh = function() {
	this.m_vectorCoreSubmesh = [];
	this.m_name = '';
	this.m_filename = '';
};

/**
	Add a core submesh to the core mesh.<br />
	This function adds a core submesh to the core mesh instance.
	@param {Cal3D.CalCoreSubmesh} coreSubmesh The core submesh to be added.
	@returns {number} ID of the added submesh. 
*/
Cal3D.CalCoreMesh.prototype.addCoreSubmesh = function(coreSubmesh) {
	var submeshId = this.m_vectorCoreSubmesh.length;
	this.m_vectorCoreSubmesh.push(coreSubmesh);
	return submeshId;
};

/**
	Get a core submesh.<br />
	This function returns the core submesh with the given ID.
	@param {number} coreSubmeshId ID of the core submesh.
	@returns {Cal3D.CalCoreSubmesh} The core submesh instance; null if the core submesh does not exist.
*/
Cal3D.CalCoreMesh.prototype.getCoreSubmesh = function(coreSubmeshId) {
	if(coreSubmeshId < 0 || coreSubmeshId >= this.m_vectorCoreSubmesh.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremesh.js');
		return null;
	}

	return this.m_vectorCoreSubmesh[coreSubmeshId];
};

/**
	Get the number of core submeshes.<br />
	This function returns the number of core submeshes in the core mesh instance.
	@returns {number} The number of core submeshes.
*/
Cal3D.CalCoreMesh.prototype.getCoreSubmeshCount = function() {
	return this.m_vectorCoreSubmesh.length;
};

/**
	Get the core submesh array.<br />
	This function returns the array that contains all core submeshes of the core mesh instance.
	@returns {Array} The core submeshes array.
*/
Cal3D.CalCoreMesh.prototype.getVectorCoreSubmesh = function() {
	return this.m_vectorCoreSubmesh;
};

/**
	Add a blend target.<br />
	This function adds a core mesh as a blend target. <br />
	It adds appropriate CalCoreSubMorphTargets to each of the core submeshes.
	@param {Cal3D.CalCoreMesh} the core mesh that should become a blend target.
	@returns {number} ID of the added blend target; -1 if any error happened.
*/
Cal3D.CalCoreMesh.prototype.addAsMorphTarget = function(coreMesh) {
	/*
		Check if the numbers of vertices allow a blending
	*/

	var otherVectorCoreSubmesh = coreMesh.getVectorCoreSubmesh();
	if(this.m_vectorCoreSubmesh.length != otherVectorCoreSubmesh.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INTERNAL, 'coremesh.js');
		return -1;
	}
	if(this.m_vectorCoreSubmesh.length == 0) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INTERNAL, 'coremesh.js');
		return -1;
	}

	var submeshId = 0;
	var subMorphTargetId = this.m_vectorCoreSubmesh[submeshId].getCoreSubMorphTargetCount();
	while(submeshId < this.m_vectorCoreSubmesh.length) {
		if(this.m_vectorCoreSubmesh[submeshId].getVertexCount() != otherVectorCoreSubmesh[submeshId].getVertexCount()) {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INTERNAL, 'coremesh.js');
			return -1;
		}
		submeshId++;
	}

	/* 
		Add the blend targets to each of the core sub meshes
	*/

	submeshId = 0;
	while(submeshId < this.m_vectorCoreSubmesh.length) {
		var vertexCount = otherVectorCoreSubmesh[submeshId].getVertexCount();
		var coreSubMorphTarget = new Cal3D.CalCoreSubMorphTarget;
		if(!coreSubMorphTarget.reserve(vertexCount))
			return -1;

		var vectorVertex = otherVectorCoreSubmesh[submeshId].getVectorVertex();
		var vertexId = 0;
		for(var i=0; i<vertexCount; i++) {
			var blendVertex = new Cal3D.CalCoreSubMorphTarget.BlendVertex;
			blendVertex.position.assign(vectorVertex[vertexId].position);
			blendVertex.normal.assign(vectorVertex[vertexId].normal);
			if(!coreSubMorphTarget.setBlendVertex(i, blendVertex))
				return -1;

			vertexId++;
		}

		this.m_vectorCoreSubmesh[submeshId].addCoreSubMorphTarget(coreSubMorphTarget);
		submeshId++;
	}

	return subMorphTargetId;
};

/**
	Scale the Mesh.<br />
	This function rescale all the data that are in the core mesh instance.
	@param {number} factor The scale factor.
*/
Cal3D.CalCoreMesh.prototype.scale = function(factor) {
	for(var submeshId=0; submeshId<this.m_vectorCoreSubmesh.length; submeshId++) {
		this.m_vectorCoreSubmesh[submeshId].scale(factor);
	}
};

/**
	Set the name of the file in which the core mesh is stored, if any.<br />
	@param {string} filename The file name.
*/
Cal3D.CalCoreMesh.prototype.setFilename = function(filename) {
	this.m_filename = filename;
};

/**
	Get the name of the file in which the core mesh is stored, if any.<br />
	@returns {string} The file name. An empty string will be returned if the mesh is not stored in a file.
*/
Cal3D.CalCoreMesh.prototype.getFilename = function() {
	return this.m_filename;
};

/**
	Set the symbolic name of the core mesh.<br />
	@param {string} name The symbolic name to be set.
*/
Cal3D.CalCoreMesh.prototype.setName = function(name) {
	this.m_name = name;
};

/**
	Get the symbolic name of the core mesh.<br />
	@returns {string} The symbolic name of the core mesh. an empty string will be returned if the symbolic name is not set yet.
*/
Cal3D.CalCoreMesh.prototype.getName = function() {
	return this.m_name;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalCoreBone
*/
Cal3D.CalCoreBone = function(name) {
	this.m_name = name;
	this.m_coreSkeleton = null;
	this.m_parentId = -1;
	this.m_listChildId = [];
	this.m_translation = new Cal3D.CalVector;
	this.m_rotation = new Cal3D.CalQuaternion;
	this.m_translationAbsolute = new Cal3D.CalVector;
	this.m_rotationAbsolute = new Cal3D.CalQuaternion;
	this.m_translationBoneSpace = new Cal3D.CalVector;
	this.m_rotationBoneSpace = new Cal3D.CalQuaternion;
	this.m_userData = null;

	this.m_boundingBox = new Cal3D.CalBoundingBox;
	this.m_boundingPosition = [ 
		new Cal3D.CalVector, 
		new Cal3D.CalVector, 
		new Cal3D.CalVector, 
		new Cal3D.CalVector, 
		new Cal3D.CalVector, 
		new Cal3D.CalVector 
	];
	this.m_boundingBoxPrecomputed = false;
};

/**
	Add a child ID.<br />
	This function adds a core bone ID to the child ID list of the core bone instance.
	@param {number} childId The ID of the core bone ID that shoud be added to the child ID list.s
	@returns {boolean} false if any error happeded.
*/
Cal3D.CalCoreBone.prototype.addChildId = function(childId) {
	this.m_listChildId.push(childId);
	return true;
};

/**
	Calculate the current state.<br />
	This function calculates the current state (absolute translation and rotation) of the core bone instance and all its children.
*/
Cal3D.CalCoreBone.prototype.calculateState = function() {
	if(this.m_parentId == -1) {
		// no parent, this means absolute state == relative state
		this.m_translationAbsolute.assign(this.m_translation);
		this.m_rotationAbsolute.assign(this.m_rotation);
	}
	else {
		// get the parent bone
		var parent = this.m_coreSkeleton.getCoreBone(this.m_parentId);

		// transform relative state with the absolute state of the parent
		this.m_translationAbsolute.assign(this.m_translation);
		this.m_translationAbsolute.multQuaternionLocal(parent.getRotationAbsolute());
		this.m_translationAbsolute.addLocal(parent.getTranslationAbsolute());

		this.m_rotationAbsolute.assign(this.m_rotation);
		this.m_rotationAbsolute.multQuaternionLocal(parent.getRotationAbsolute());
	}

	// calculate all child bones
	for(var iChildId=0; iChildId<this.m_listChildId.length; iChildId++) {
		this.m_coreSkeleton.getCoreBone(this.m_listChildId[iChildId]).calculateState();
	}
};

/**
	Get the child ID list.<br />
	This function returns the list that contains all child IDs of the core bone instance.
	@returns {Array} The child ID list.
*/
Cal3D.CalCoreBone.prototype.getListChildId = function() {
	return this.m_listChildId;
};

/**
	Get name of the core bone.<br />
	This function returns the name of the core bone instance.
	@returns {string} The name of the core bone.
*/
Cal3D.CalCoreBone.prototype.getName = function() {
	return this.m_name;
};

/**
	Get the parent ID.<br />
	This function returns the parent ID of the core bone instance.
	@returns {number} The parent ID, or -1 if this is a root core bone.
*/
Cal3D.CalCoreBone.prototype.getParentId = function() {
	return this.m_parentId;
};

/**
	Get the core skeleton the bone is attached to.<br />
	This function returns the core skeleton this bone is attached to.
	@returns {Cal3D.CalCoreSkeleton} The core skeleton.
*/
Cal3D.CalCoreBone.prototype.getCoreSkeleton = function() {
	return this.m_coreSkeleton;
};

/**
	Ger the rotation.<br />
	This function returns the relative rotation of the core bone instance.
	@returns {Cal3D.CalQuaternion} The relative rotation to the parent as quaternion.
*/
Cal3D.CalCoreBone.prototype.getRotation = function() {
	return this.m_rotation;
};

/**
	Get the absolute rotation.<br />
	This function returns the absolute rotation of the core bone instance.
	@returns {Cal3D.CalQuaternion} The absolute rotation as quaternion.
*/
Cal3D.CalCoreBone.prototype.getRotationAbsolute = function() {
	return this.m_rotationAbsolute;
};

/**
	Get the bone space rotation.<br />
	This function returns the rotation to bring a point into the core bone instance space.
	@returns {Cal3D.CalQuaternion} The bone space rotation as quaternion.
*/
Cal3D.CalCoreBone.prototype.getRotationBoneSpace = function() {
	return this.m_rotationBoneSpace;
};

/**
	Get the translation.<br />
	This function returns the relative translation of the core bone instance.
	@returns {Cal3D.CalVector} The relative translation to the parent as vector.
*/
Cal3D.CalCoreBone.prototype.getTranslation = function() {
	return this.m_translation;
};

/**
	Get the absolute translation.<br />
	This function returns the absolute translation of the core bone instance.
	@returns {Cal3D.CalVector} The absolute translation as vector.
*/
Cal3D.CalCoreBone.prototype.getTranslationAbsolute = function() {
	return this.m_translationAbsolute;
};

/**
	Get the bone space translation.<br />
	This function returns the translation to bring a point into the core bone instance space.
	@returns {Cal3D.CalVector} The bone space translation as vector.
*/
Cal3D.CalCoreBone.prototype.getTranslationBoneSpace = function() {
	return this.m_translationBoneSpace;
};

/**
	Get the user data.<br />
	This function returns the user data stored in the core bone instance.
	@returns {object} The user data stored in the core bone.
*/
Cal3D.CalCoreBone.prototype.getUserData = function() {
	return this.m_userData;
};

/**
	Set the core skeleton the bone is attached to.<br />
	This function sets the core skeleton to which the core bone instance is attached to.
	@param {Cal3D.CalCoreSkeleton} coreSkeleton The core skeleton to which the core bone instance should be attached to.
*/
Cal3D.CalCoreBone.prototype.setCoreSkeleton = function(coreSkeleton) {
	this.m_coreSkeleton = coreSkeleton;
};

/**
	Set the parent bone ID.<br />
	This function sets the parent ID of the core bone instance.
	@param {number} parentId The ID of the parent to be set.
*/
Cal3D.CalCoreBone.prototype.setParentId = function(parentId) {
	this.m_parentId = parentId;
};

/**
	Set the rotation.<br />
	This function sets the relative rotation of the core bone instance.
	@param {Cal3D.CalQuaternion} rotation The relative rotation to the parent as quaternion.
*/
Cal3D.CalCoreBone.prototype.setRotation = function(rotation) {
	this.m_rotation.assign(rotation);
};

/**
	Set the bone space rotation.<br />
	This function sets the rotation that brings a point into the core bone instance space.
	@param {Cal3D.CalQuaternion} rotation The bone space rotation to be set as quaternion.
*/
Cal3D.CalCoreBone.prototype.setRotationBoneSpace = function(rotation) {
	this.m_rotationBoneSpace.assign(rotation);
};

/**
	Set the translation.<br />
	This function sets the relative translation of the core bone instance.
	@param {Cal3D.CalVector} translation The relative translation to the parent as vector.
*/
Cal3D.CalCoreBone.prototype.setTranslation = function(translation) {
	this.m_translation.assign(translation);
};

/**
	Set the bone space translation.<br />
	This function sets the translation that brings a point into the core bone instance space.
	@param {Cal3D.CalVector} translation The bone space translation to be set as vector.
*/
Cal3D.CalCoreBone.prototype.setTranslationBoneSpace = function(translation) {
	this.m_translationBoneSpace.assign(translation);
};

/**
	Set user data that should be stored inthis core bone.<br />
	This function stores user data in the core bone instance.
	@param {object} userData The user data to be stored.
*/
Cal3D.CalCoreBone.prototype.setUserData = function(userData) {
	this.m_userData = userData;
};

/**
	Calculate the bounding box.<br />
	This function Calculates the bounding box of the core bone instance.
	@param {Cal3D.CalCoreModel} coreModel The core model needed to retrieve vertices.
*/
Cal3D.CalCoreBone.prototype.calculateBoundingBox = function(coreModel) {
	var boneId = this.m_coreSkeleton.getCoreBoneId(this.m_name);
	var bBoundsComputed = false;

	var rot = new Cal3D.CalQuaternion(this.m_rotationBoneSpace);
	rot.invert();

	var dir = new Cal3D.CalVector(1, 0, 0);
	dir.multQuaternionLocal(rot);
	this.m_boundingBox.plane[0].setNormal(dir);

	dir.assign(-1, 0, 0);
	dir.multQuaternionLocal(rot);
	this.m_boundingBox.plane[1].setNormal(dir);

	dir.assign(0, 1, 0);
	dir.multQuaternionLocal(rot);
	this.m_boundingBox.plane[2].setNormal(dir);

	dir.assign(0, -1, 0);
	dir.multQuaternionLocal(rot);
	this.m_boundingBox.plane[3].setNormal(dir);

	dir.assign(0, 0, 1);
	dir.multQuaternionLocal(rot);
	this.m_boundingBox.plane[4].setNormal(dir);

	dir.assign(0, 0, -1);
	dir.multQuaternionLocal(rot);
	this.m_boundingBox.plane[5].setNormal(dir);

	var coreMeshCount = coreModel.getCoreMeshCount();
	for(var meshId=0; meshId<coreMeshCount; meshId++) {
		var coreMesh = coreModel.getCoreMesh(meshId);

		var coreSubmeshCount = coreMesh.getCoreSubmeshCount();
		for(var submeshId=0; submeshId<coreSubmeshCount; submeshId++) {
			var coreSubmesh = coreMesh.getCoreSubmesh(submeshId);

			if(coreSubmesh.getSpringCount() == 0) {
				var vectorVertex = coreSubmesh.getVectorVertex();
				for(var vertexId=0; vertexId<vectorVertex.length; vertexId++) {
					var vertexInfluenceCount = vectorVertex[vertexId].vectorInfluence.length;
					for(var influenceId=0; influenceId<vertexInfluenceCount; influenceId++) {
						if(vectorVertex[vertexId].vectorInfluence[influenceId].boneId == boneId && vectorVertex[vertexId].vectorInfluence[influenceId].weight > 0.5) {
							for(var planeId=0; planeId<6; planeId++) {
								if(this.m_boundingBox.plane[planeId].eval(vectorVertex[vertexId].position) < 0) {
									this.m_boundingBox.plane[planeId].setPosition(vectorVertex[vertexId].position);
									this.m_boundingPosition[planeId].assign(vectorVertex[vertexId].position);
									bBoundsComputed = true;
								}
							}
						}
					}
				}	
			}
		}
	}

	// to handle bones with no vertices assigned 
	if(!bBoundsComputed) {
		for(var planeId=0; planeId<6; planeId++) {
			this.m_boundingBox.plane[planeId].setPosition(this.m_translation); 
			this.m_boundingPosition[planeId].assign(this.m_translation); 
		} 
	} 

	this.m_boundingBoxPrecomputed = true;
};

/**
	Get the current bounding box.<br />
	This function returns the current bounding box of the core bone instance.
	@returns {Cal3D.CalBoundingBox} The current bounding box of the core bone.
*/
Cal3D.CalCoreBone.prototype.getBoundingBox = function() {
	return this.m_boundingBox;
};

/**
	@private
*/
Cal3D.CalCoreBone.prototype.getBoundingData = function(planeId, position) {
	if(!position)
		position = new Cal3D.CalVector;

	position.assign(this.m_boundingPosition[planeId]);
	return position;
};

/**
	@private
*/
Cal3D.CalCoreBone.prototype.isBoundingBoxPrecomputed = function() {
	return this.m_boundingBoxPrecomputed;
};

/**
	Scale the core bone.<br />
	This function rescale all the data that are in the core bone instance and in all its children.
	@param {number} factor The scale factor.
*/
Cal3D.CalCoreBone.prototype.scale = function(factor) {
	this.m_translation.multScalarLocal(factor);
	this.m_translationAbsolute.multScalarLocal(factor);
	this.m_translationBoneSpace.multScalarLocal(factor);

	// calculate all child bones
	for(var iChildId=0; iChildId<this.m_listChildId.length; iChildId++)	{
		this.m_coreSkeleton.getCoreBone(this.m_listChildId[iChildId]).scale(factor);
	}
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalCoreSkeleton
*/
Cal3D.CalCoreSkeleton = function() {
	this.m_vectorCoreBone = [];
	this.m_mapCoreBoneNames = {};
	this.m_vectorRootCoreBoneId = [];
};

/**
	Add a core bone.<br />
	This function adds a core bone to the core skeleton instance.
	@param {Cal3D.CalCoreBone} coreBone The core bone to be added.
	@returns {number} ID of the added core bone.
*/
Cal3D.CalCoreSkeleton.prototype.addCoreBone = function(coreBone) {
	// get next bone id
	var boneId = this.m_vectorCoreBone.length;
	this.m_vectorCoreBone.push(coreBone);

	// if necessary, add the core bone to the root bone list
	if(coreBone.getParentId() == -1) {
		this.m_vectorRootCoreBoneId.push(boneId);
	}

	// add a reference from the bone's name to its id
	this.mapCoreBoneName(boneId, coreBone.getName());

	return boneId;
};

/**
	Calculate the current state.<br />
	This function calculates the current state of the core skeleton instance by calculating all the core bone states.
*/
Cal3D.CalCoreSkeleton.prototype.calculateState = function() {
	// calculate all bone states of the skeleton
	for(var iRootCoreBoneId=0; iRootCoreBoneId<this.m_vectorRootCoreBoneId.length; iRootCoreBoneId++) {
		this.m_vectorCoreBone[this.m_vectorRootCoreBoneId[iRootCoreBoneId]].calculateState();
	}
};

/**
	Get a core bone.<br />
	This function returns the core bone with the given name or ID.
	@param {string | number} coreBoneId The name or ID of the core bone.
	@returns {Cal3D.CalCoreBone} The core bone.
*/
Cal3D.CalCoreSkeleton.prototype.getCoreBone = function(coreBoneId) {
	if((typeof coreBoneId) == 'string')
		coreBoneId = this.getCoreBoneId(coreBoneId);

	if(coreBoneId < 0 || coreBoneId >= this.m_vectorCoreBone.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coreskeleton.js');
		return null;
	}

	return this.m_vectorCoreBone[coreBoneId];
};

/**
	Get the ID of a specified core bone.<br />
	This function returns the ID of a specified core bone.
	@param {string} name The name of the core bone whose ID should be returned.
	@returns {number} The ID associated with the given name.
*/
Cal3D.CalCoreSkeleton.prototype.getCoreBoneId = function(name) {
	var coneBoneId = this.m_mapCoreBoneNames[name];
	// See if this mapping exists
	if(coneBoneId == undefined) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coreskeleton.js');
		return -1;
	}

	return coneBoneId;
};

/**
	Map the name to a specific ID of a core bone.<br />
	This function returns true or false depending on whether the mapping was successful or not. Note that it is possible to overwrite 
	and existing mapping and no error will be given.
	@param {number} coreBoneId ID of the core bone to be associated with the name.
	@param {string} name The name of the core bone that will be associated with the ID.
	@returns {boolean} true if succeeded; false if failed.
*/
Cal3D.CalCoreSkeleton.prototype.mapCoreBoneName = function(coreBoneId, name) {
	// Make sure the given ID is a valid corebone ID
	if(coreBoneId < 0 || coreBoneId >= this.m_vectorCoreBone.length)
		return false;

	this.m_mapCoreBoneNames[name] = coreBoneId;
	return true;
};

/**
	Get the root core bone ID list.<br />
	This function returns the list that contains all root core bone IDs of the core skeleton instance.
	@returns {Array} The root core bone ID list.
*/
Cal3D.CalCoreSkeleton.prototype.getVectorRootCoreBoneId = function() {
	return this.m_vectorRootCoreBoneId;
};

/**
	Get the core bone list.<br />
	This function returns the vector that contains all core bones of the core skeleton instance.
	@returns {Array} The core bone list.
*/
Cal3D.CalCoreSkeleton.prototype.getVectorCoreBone = function() {
	return this.m_vectorCoreBone;
};

/**
	Calculate bounding boxes of core bones.<br />
	This function calculates the bounding box of every bone in the core skeleton.
	@param {Cal3D.CalCoreModel} coreModel The coreModel needed to retrieve vertices.
*/
Cal3D.CalCoreSkeleton.prototype.calculateBoundingBoxes = function(coreModel) {
	for(var boneId=0; boneId<this.m_vectorCoreBone.length; boneId++) {
		print('Calc Bone '+boneId);
		this.m_vectorCoreBone[boneId].calculateBoundingBox(coreModel);
	}
};

/**
	Scale the core skeleton.<br />
	This function rescale all the data that are in the core skeleton instance.
	@param {number} The scale factor.
*/
Cal3D.CalCoreSkeleton.prototype.scale = function(factor) {
	for(var iRootCoreBoneId=0; iRootCoreBoneId<this.m_vectorRootCoreBoneId.length; iRootCoreBoneId++) {
		this.m_vectorCoreBone[iRootCoreBoneId].scale(factor);
	}
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalCoreModel
*/
Cal3D.CalCoreModel = function(name) {
	this.m_name = name;
	this.m_coreSkeleton = null;
	this.m_vectorCoreAnimation = [];
	this.m_vectorCoreMorphAnimation = [];
	this.m_vectorCoreMesh = [];
	this.m_vectorCoreMaterial = [];
	this.m_mapmapCoreMaterialThread = {};
	this.m_animationName = {};
	this.m_materialName = {};
	this.m_meshName = {};
	this.m_userData = null;
};

/**
	Get the user data<br />
	This function returns the user data stored in the core model instance.
	@returns {object} The user data stored in the core model.
*/
Cal3D.CalCoreModel.prototype.getUserData = function() {
	return this.m_userData;
};

/**
	Store user data.<br />
	This function stores user data in the core model instance.
	@param {object} userData The user data to be stored.
*/
Cal3D.CalCoreModel.prototype.setUserData = function(userData) {
	this.m_userData = userData;
};

/**
	Scale the core model.<br />
	This function rescale all data that are in the core model instance.
	@param {number} factor The scale factor.
*/
Cal3D.CalCoreModel.prototype.scale = function(factor) {
	this.m_coreSkeleton.scale(factor);

	for(var animationId=0; animationId<this.m_vectorCoreAnimation.length; animationId++) {
		this.m_vectorCoreAnimation[animationId].scale(factor);
	}

	for(var meshId=0; meshId<this.m_vectorCoreMesh.length; meshId++) {
		this.m_vectorCoreMesh[meshId].scale(factor);
	}
};

/**
	Add a core animation to the core model.<br />
	This function adds a core animation to the core model instance.
	@param {Cal3d.CalCoreAnimation} coreAnimation The core animation instance to be added.
	@returns {number} ID of the added core animation.
*/
Cal3D.CalCoreModel.prototype.addCoreAnimation = function(coreAnimation) {
	var animationId = this.m_vectorCoreAnimation.length;
	this.m_vectorCoreAnimation.push(coreAnimation);
	return animationId;
};

/**
	Get a core animation.<br />
	This function returns the core animation with the given ID.
	@param {number} coreAnimationId ID of the core animation.
	@returns {Cal3d.CalCoreAnimation} The core animation; null if it does not exist.
*/
Cal3D.CalCoreModel.prototype.getCoreAnimation = function(coreAnimationId) {
	if(coreAnimationId < 0 || coreAnimationId >= this.m_vectorCoreAnimation.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return null;
	}

	return this.m_vectorCoreAnimation[coreAnimationId];
};

/**
	Get the number of core animations.<br />
	This function returns the number of core animations in the core model instance.
	@returns {number} The number of core animations.
*/
Cal3D.CalCoreModel.prototype.getCoreAnimationCount = function() {
	return this.m_vectorCoreAnimation.length;
};

Cal3D.CalCoreModel.prototype.loadCoreAnimation = function(filename) {
	throw 'not implemented error';
};

/**
	Delete the resources used by the named core animation.<br />
	The name must be associated with a valid core animation ID with the function getAnimationId(). The caller must ensure that the 
	corresponding is not referenced anywhere otherwise unpredictable results will occur.
	@param {number | string} ID or name of the core animation.
	@returns {number} ID of the unloaded core animation; -1 if the core animation does not exist.
*/
Cal3D.CalCoreModel.prototype.unloadCoreAnimation = function(coreAnimationId) {
	if((typeof coreAnimationId) == 'string')
		coreAnimationId = this.getCoreAnimationId(coreAnimationId);

	if(coreAnimationId < 0 || coreAnimationId >= this.m_vectorCoreAnimation.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return -1;
	}

	this.m_vectorCoreAnimation[coreAnimationId] = null;
	return coreAnimationId;
};

Cal3D.CalCoreModel.prototype.saveCoreAnimation = function(filename, coreAnimationId) {
	throw 'not implemented error';

	// check if the core animation id is valid
	if(coreAnimationId < 0 || coreAnimationId >= this.m_vectorCoreAnimation.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return false;
	}

	// save the core animation
	if(!Cal3D.CalSaver.saveCoreAnimation(filename, this.m_vectorCoreAnimation[coreAnimationId]))
		return false;

	return true;
};

/**
	Create or overwrite a string-to-animation ID mapping.<br />
	This function makes an animation ID reference-able by a string name.<br />
	Note that we don't verify that the ID is valid because the animation may be added later. Also, if there is already a helper
	with this name, it will be overwritten without warning.
	@param {string} name The (new) name that will be associated with the ID.
	@param {number} coreAnimationId The ID of the animation to be referenced by the given name.
	@returns {boolean} true if succeeded; false if any error happened. 
*/
Cal3D.CalCoreModel.prototype.addAnimationName = function(name, coreAnimationId) {
	// check if the core animation id is valid
	if(coreAnimationId < 0 || coreAnimationId >= this.m_vectorCoreAnimation.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return false;
	}

	this.m_vectorCoreAnimation[coreAnimationId].setName(name);
	this.m_animationName[name] = coreAnimationId;
	return true;
};

/**
	Get the ID of the core animation by name.<br />
	@param {string} animationName Name associated with the ID.
	@returns {number} ID of the core animation; -1 if there is no ID associated with the given name.
*/
Cal3D.CalCoreModel.prototype.getCoreAnimationId = function(animationName) {
	var coreAnimationId = this.m_animationName[animationName];
	if(coreAnimationId == undefined)
		return -1;

	if(!this.getCoreAnimation(coreAnimationId))
		return -1;

	return coreAnimationId;
};

/**
	Add a core morph animation.<br />
	This function adds a core morph animation to the core model instance.
	@param {Cal3D.CalCoreMorphAnimation} coreMorphAnimation The core morph animation to be added.
	@returns {number} ID of the added core morph animation.
*/
Cal3D.CalCoreModel.prototype.addCoreMorphAnimation = function(coreMorphAnimation) {
	var morphAnimationId = this.m_vectorCoreMorphAnimation.length;
	this.m_vectorCoreMorphAnimation.push(coreMorphAnimation);
	return morphAnimationId;
};

/**
	Get a core morph animation<br />
	This function returns the core morph animation with the given ID.
	@param {number} ID of the core morph animation.
	@returns {Cal3D.CalCoreMorphAnimation} The core morph animation; null if the core morph animation does not exist.
*/
Cal3D.CalCoreModel.prototype.getCoreMorphAnimation = function(coreMorphAnimationId) {
	if(coreMorphAnimationId < 0 || coreMorphAnimationId >= this.m_vectorCoreMorphAnimation.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return null;
	}

	return this.m_vectorCoreMorphAnimation[coreMorphAnimationId];
};

/**
	Get the number of core morph animations.<br />
	This function returns the number of core morph animations in the core model instance.
	@returns {number} The number of core morph animations.. 
*/
Cal3D.CalCoreModel.prototype.getCoreMorphAnimationCount = function() {
	return this.m_vectorCoreMorphAnimation.length;
};

/**
	Add a core material.<br />
	This function adds a core material to the core model instance.
	@param {Cal3D.CalCoreMaterial} coreMaterial The core material to be added.
	@returns {number} ID of the added material.
*/
Cal3D.CalCoreModel.prototype.addCoreMaterial = function(coreMaterial) {
	var materialId = this.m_vectorCoreMaterial.length;
	this.m_vectorCoreMaterial.push(coreMaterial);
	return materialId;
};

/**
	Create a core material thread.<br />
	This function creates a new core material thread with the given ID.
	@param {number} coreMaterialThreadId The ID of the core material thread to be created.
	@returns {boolean} true if succeeded; false if any error happened. 
*/
Cal3D.CalCoreModel.prototype.createCoreMaterialThread = function(coreMaterialThreadId) {
	// insert an empty core material thread with the given id
	this.m_mapmapCoreMaterialThread[coreMaterialThreadId] = {};
	return true;
};

/**
	Get a core material.<br />
	This function returns the core material with the given ID.
	@param {number} coreMaterialId The ID of the core material.
	@returns {Cal3D.CalCoreMaterial} The core material; null if it does not exist.
*/
Cal3D.CalCoreModel.prototype.getCoreMaterial = function(coreMaterialId) {
	if(coreMaterialId < 0 || coreMaterialId >= this.m_vectorCoreMaterial.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return null;
	}

	return this.m_vectorCoreMaterial[coreMaterialId];
};

/**
	Get the number of core materials.<br />
	This function returns the number of core materials in the core model instance.
	@returns {number} The number of core materials.
*/
Cal3D.CalCoreModel.prototype.getCoreMaterialCount = function() {
	return this.m_vectorCoreMaterial.length;
};

/**
	Get a specified core material ID.<br />
	This function returns the core material ID for a specified core material name or thread/core material set pair.
	@param {string | number} materialName The name that is associated with a core material ID, or the ID of the core material thread.
	@param {number} coreMaterialSetId (Optional) The ID of the core material set.
	@returns {number} ID of the core material; -1 if it does not exist.
*/
Cal3D.CalCoreModel.prototype.getCoreMaterialId = function(materialName) {
	if(arguments.length == 1) {
		var coreMaterialId = this.m_materialName[materialName];
		if(coreMaterialId == undefined)
			return -1;

		if(!this.getCoreMaterial(coreMaterialId))
			return -1;

		return coreMaterialId;
	}
	else if(arguments.length == 2) {
		var coreMaterialThreadId = arguments[0];
		var coreMaterialSetId = arguments[1];

		// find the core material thread
		var coreMaterialThread = this.m_mapmapCoreMaterialThread[coreMaterialThreadId];
		if(coreMaterialThread == undefined) {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
			return -1;
		}

		// find the core material id for the given set
		var coreMaterialId = coreMaterialThread[coreMaterialSetId];
		if(coreMaterialId == undefined) {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
			return -1;
		}

		return coreMaterialId;
	}

	return -1;
};

Cal3D.CalCoreModel.prototype.loadCoreMaterial = function(filename) {
	throw 'not implemented error';
};

/**
	Delete the resources used by the named core material.<br />
	The name must be associated with a valid core material ID with the function getMaterialId(). The caller must ensure that 
	the corresponding is not referenced anywhere otherwise unpredictable results will occur.
	@param {string | number} coreMaterialId The symbolic name or ID of the core material to unload.
	@returns {number} ID of the unloaded core material; -1 if it does not exist.
*/
Cal3D.CalCoreModel.prototype.unloadCoreMaterial = function(coreMaterialId) {
	if((typeof coreMaterialId) == 'string')
		coreMaterialId = this.getCoreMaterialId(coreMaterialId);

	if(coreMaterialId < 0 || coreMaterialId >= this.m_vectorCoreMaterial.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return -1;
	}

	this.m_vectorCoreMaterial[coreMaterialId] = null;
	return coreMaterialId;
};

Cal3D.CalCoreModel.prototype.saveCoreMaterial = function(filename, coreMaterialId) {
	throw 'not implemented error';

	// check if the core material id is valid
	if(coreMaterialId < 0 || coreMaterialId >= this.m_vectorCoreMaterial.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return false;
	}

	// save the core animation
	return Cal3D.CalSaver.saveCoreMaterial(filename, this.m_vectorCoreMaterial[coreMaterialId]);
};

/**
	Set a core material ID.<br />
	This function sets a core material ID for a core material thread/core material set pair.
	@param {number} coreMaterialThreadId The ID of the core material thread.
	@param {number} coreMaterialSetId The ID of the core maetrial set.
	@param {number} coreMaterialId The ID of the core maetrial.
	@returns {boolean} true if succeeded; false if failed.
*/
Cal3D.CalCoreModel.prototype.setCoreMaterialId = function(coreMaterialThreadId, coreMaterialSetId, coreMaterialId) {
	// find the core material thread
	var coreMaterialThread = this.m_mapmapCoreMaterialThread[coreMaterialThreadId];
	if(coreMaterialThread == undefined) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return false;
	}

	// set the given set id in the core material thread to the given core material id
	coreMaterialThread[coreMaterialSetId] = coreMaterialId
	return true;
};

/**
	Create or overwrite a string-to-core-material ID mapping.<br />
	This function makes a core material ID reference-able by a string name.<br />
	Note that we don't verify that the ID is valid because the material may be added later. Also, if there is already a helper 
	with this name, it will be overwritten without warning.
	@param {string} name The name that will be associated with the ID.
	@param {number} coreMaterialId The core ID number of the material to be referenced by the name.
	@returns {boolean} true if succeeded; false if any error happened.
*/
Cal3D.CalCoreModel.prototype.addMaterialName = function(name, coreMaterialId) {
	if(coreMaterialId < 0 || coreMaterialId >= this.m_vectorCoreMaterial.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return false;
	}

	this.m_vectorCoreMaterial[coreMaterialId].setName(name);
	this.m_materialName[name] = coreMaterialId;
	return true;
};

/**
	Add a core mesh.<br />
	This function adds a core mesh to the core model instance.
	@param {Cal3D.CalCoreMesh} coreMesh The core mesh to be added.
	@returns {number} ID of the added core mesh.
*/
Cal3D.CalCoreModel.prototype.addCoreMesh = function(coreMesh) {
	var meshId = this.m_vectorCoreMesh.length;
	this.m_vectorCoreMesh.push(coreMesh);
	return meshId;
};

/**
	Get a core mesh.<br />
	This function returns the core mesh with the given ID.
	@param {number} coreMeshId The ID of the core mesh.
	@returns {Cal3D.CalCoreMesh} The core mesh; null if the core mesh does not exist.
*/
Cal3D.CalCoreModel.prototype.getCoreMesh = function(coreMeshId) {
	if(coreMeshId < 0 || coreMeshId >= this.m_vectorCoreMesh.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return null;
	}

	return this.m_vectorCoreMesh[coreMeshId];
};

/**
	Get the number of core meshes.<br />
	This function returns the number of core meshes in the core model instance.
	@returns {number} The number of core meshes.
*/
Cal3D.CalCoreModel.prototype.getCoreMeshCount = function() {
	return this.m_vectorCoreMesh.length;
};

Cal3D.CalCoreModel.prototype.loadCoreMesh = function(filename) {
	throw 'not implemented error';
};

/**
	Delete the resources used by the named core mesh.<br />
	The name must be associated with a valid core mesh Id with the function getMeshId(). The caller must ensure that the
	corresponding is not referenced anywhere otherwise unpredictable results will occur.
	@param {string | number} The symbolic name or ID of the core mesh to unload.
	@returns {number} ID of the unloaded core mesh; -1 if it does not exist.
*/
Cal3D.CalCoreModel.prototype.unloadCoreMesh = function(coreMeshId) {
	if((typeof coreMeshId) == 'string')
		coreMeshId = this.getCoreMeshId(coreMeshId);

	if(coreMeshId < 0 || coreMeshId >= this.m_vectorCoreMesh.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return -1;
	}

	this.m_vectorCoreMesh[coreMeshId] = null;
	return coreMeshId;
};

Cal3D.CalCoreModel.prototype.saveCoreMesh = function(filename, coreMeshId) {
	throw 'not implemented error';

	// check if the core mesh id is valid
	if(coreMeshId < 0 || coreMeshId >= this.m_vectorCoreMesh.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return false;
	}

	// save the core mesh
	return Cal3D.CalSaver.saveCoreMesh(filename, this.m_vectorCoreMesh[coreMeshId]);
};

/**
	Create or overwrite a string-to-core-mesh ID mapping.<br />
	This function makes a core mesh ID reference-able by a string name.<br />
	Note that we don't verify that the ID is valid because the mesh may be added later. Also, if there is already a helper with 
	this name, it will be overwritten without warning.
	@param {string} meshName The name that will be associated with the ID.
	@param {number} coreMeshId The core ID number of the mesh to be referenced by the name.
	@returns {boolean} true if succeeded; false if failed.
*/
Cal3D.CalCoreModel.prototype.addMeshName = function(meshName, coreMeshId) {
	if(coreMeshId < 0 || coreMeshId >= this.m_vectorCoreMesh.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return false;
	}

	this.m_vectorCoreMesh[coreMeshId].setName(meshName);
	this.m_meshName[meshName] = coreMeshId;
	return true;
};

/**
	Get the ID of the core mesh referenced by name.<br />
	@param {string} meshName The name that is associated with a core mesh ID.
	@returns {number} The core mesh ID associated with the given name; -1 if it does not exist.
*/
Cal3D.CalCoreModel.prototype.getCoreMeshId = function(meshName) {
	var coreMeshId = this.m_meshName[meshName];
	if(coreMeshId == undefined)
		return -1;

	if(!this.getCoreMesh(coreMeshId))
		return -1;

	return coreMeshId;
};

/**
	Get the core skeleton.<br />
	This function returns the core skeleton of the core model instance.
	@returns {Cal3D.CalCoreSkeleton} The core skeleton.
*/
Cal3D.CalCoreModel.prototype.makemodel = function() {
	return new Cal3D.CalModel(this);
};

Cal3D.CalCoreModel.prototype.getCoreSkeleton = function() {
	return this.m_coreSkeleton;
};

Cal3D.CalCoreModel.prototype.loadCoreSkeleton = function(filename) {
	throw 'not implemented error';
};

Cal3D.CalCoreModel.prototype.saveCoreSkeleton = function(filename) {
	throw 'not implemented error';

	// check if we have a core skeleton in this code model
	if(this.m_coreSkeleton) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return false;
	}

	// save the core skeleton
	return Cal3D.CalSaver.saveCoreSkeleton(filename, this.m_coreSkeleton);
};

/**
	Set the core skeleton of the core model.<br />
	This function sets the core skeleton of the core model instance.
	@param {Cal3D.CalCoreSkeleton} coreSkeleton The core skeleton to be set.
*/
Cal3D.CalCoreModel.prototype.setCoreSkeleton = function(coreSkeleton) {
	if(!coreSkeleton) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coremodel.js');
		return;
	}

	this.m_coreSkeleton = coreSkeleton;
};

/**
	Create or overwrite a string-to-boneId mapping.<br />
	This function makes a bone ID reference-able by a string name.
	@param {string} boneName The that will be associated with the ID.
	@param {number} boneId The ID of the bone that will be referenced by the given name.
*/
Cal3D.CalCoreModel.prototype.addBoneName = function(boneName, boneId) {
	// make sure the skeleton has been loaded first
	if(this.m_coreSkeleton) {
		// map the bone ID to the name
		this.m_coreSkeleton.mapCoreBoneName(boneId, boneName);
	}
};

/**
	Get the ID of the bone referenced by a name.<br />
	@param {string} boneName The name that is associated with a bone ID.
	@returns {number} The bone ID associated with the given name; -1 if it does not exist.
*/
Cal3D.CalCoreModel.prototype.getBoneId = function(boneName) {
	if(this.m_coreSkeleton) {
		return this.m_coreSkeleton.getCoreBoneId(boneName);
	}

	return -1;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalCoreSubMorphTarget
*/
Cal3D.CalCoreSubMorphTarget = function() {
	this.m_vectorBlendVertex = [];
};

/**
	Get the number of blend vertices.<br />
	This function returns the number of blend vertices in the core sub morph target instance.
	@returns {number} The number of blend vertices.
*/
Cal3D.CalCoreSubMorphTarget.prototype.getBlendVertexCount = function() {
	return this.m_vectorBlendVertex.length;
};

/**
	Get the blend vertex list.<br />
	This function returns the list that contains all blend vertices of the core sub morph target instance.
	@returns {Array} The blend vertex list.
*/
Cal3D.CalCoreSubMorphTarget.prototype.getVectorBlendVertex = function() {
	return this.m_vectorBlendVertex;
};

/**
	Allocate space for the blend vertices.<br />
	This function reserves space for the blend vertices of the core sub morph target instance.
	@param {number} blendVertexCount The number of blend vertices that this core sub morph target should holds.
	@returns {boolean} true if succeeded; false if any error happened. 
*/
Cal3D.CalCoreSubMorphTarget.prototype.reserve = function(blendVertexCount) {
	this.m_vectorBlendVertex = new Array(blendVertexCount);
	return true;
};

/**
	Set a blend vertex.<br />
	This function sets a specified blend vertex in the core sub morph target instance.
	@param {number} blendVertexId The ID of the vertex.
	@param {Cal3D.CalCoreSubMorphTarget.BlendVertex} blendVertex The vertex to set.
	@returns {boolean} true if succeeded; false if the vertex does not exist.
*/
Cal3D.CalCoreSubMorphTarget.prototype.setBlendVertex = function(blendVertexId, blendVertex) {
	if(blendVertexId < 0 || blendVertexId >= this.m_vectorBlendVertex.length)
		return false;

	this.m_vectorBlendVertex[blendVertexId] = blendVertex;
	return true;
};



/**
	@class BlendVertex

	The blend vertex structure for the CalCoreSubMorphTarget class.
*/
Cal3D.CalCoreSubMorphTarget.BlendVertex = function() {
	this.position = new Cal3D.CalVector;
	this.normal = new Cal3D.CalVector;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalCoreMorphAnimation
*/
Cal3D.CalCoreMorphAnimation = function() {
	this.m_vectorCoreMeshID = [];
	this.m_vectorMorphTargetID = [];
};

/**
	Add a core mesh ID and a morph target ID of that core mesh.<br />
	@param {number} coreMeshID The core mesh ID to be added.
	@param {number} morphTargetID The morph target ID to be added.
	@returns {boolean} true if succeeded; false if any error happened.
*/
Cal3D.CalCoreMorphAnimation.prototype.addMorphTarget = function(coreMeshID, morphTargetID) {
	this.m_vectorCoreMeshID.push(coreMeshID);
	this.m_vectorMorphTargetID.push(morphTargetID);
	return true;
};

/**
	Get the core mesh ID list.<br />
	This function returns the list that contains all core mesh IDs of the core morph animation instance.
	@returns {Array} The the core mesh ID list.
*/
Cal3D.CalCoreMorphAnimation.prototype.getVectorCoreMeshID = function() {
	return this.m_vectorCoreMeshID;
};

/**
	Get the morph target ID list.<br />
	This function returns the list that contains all morph target  IDs of the core morph animation instance.
	@returns {Array} The morph target ID list.
*/
Cal3D.CalCoreMorphAnimation.prototype.getVectorMorphTargetID = function() {
	return this.m_vectorMorphTargetID;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalCoreKeyframe
*/
Cal3D.CalCoreKeyframe = function() {
	this.m_time = 0;
	this.m_translation = new Cal3D.CalVector;
	this.m_rotation = new Cal3D.CalQuaternion;
};

/**
	Create the core keyframe instance<br />
	@returns {boolean} true if succeeded; false if failed.
*/
Cal3D.CalCoreKeyframe.prototype.create = function() {
	return true;
};

/**
	Destroy the core keyframe instance.
*/
Cal3D.CalCoreKeyframe.prototype.destroy = function() {
};

/**
	Get the rotation.<br />
	This function returns the rotation of the core keyframe instance.
	@returns {Cal3D.CalQuaternion} The rotation as quaternion.
*/
Cal3D.CalCoreKeyframe.prototype.getRotation = function() {
	return this.m_rotation;
};

/**
	Get the translation.<br />
	This function returns the translation of the core keyframe instance.
	@returns {Cal3D.CalVector} The translation as vector.
*/
Cal3D.CalCoreKeyframe.prototype.getTranslation = function() {
	return this.m_translation;
};

/**
	Get the time.<br />
	This function returns the time of the core keyframe instance.
	@returns {number} The time in seconds.
*/
Cal3D.CalCoreKeyframe.prototype.getTime = function() {
	return this.m_time;
};

/**
	Set the rotation.<br />
	This function sets the rotation of the core keyframe instance.
	@param {Cal3D.CalQuaternion} rotation The rotation to be set.
*/
Cal3D.CalCoreKeyframe.prototype.setRotation = function(rotation) {
	this.m_rotation.assign(rotation);
};

/**
	Set the translation.<br />
	This function sets the translation of the core keyframe instance.
	@param {Cal3D.CalVector} translation The translation to be set.
*/
Cal3D.CalCoreKeyframe.prototype.setTranslation = function(translation) {
	this.m_translation.assign(translation);
};

/**
	Set the time.<br />
	This function sets the time of the core keyframe instance.
	@param {number} time The time to be set in seconds.
*/
Cal3D.CalCoreKeyframe.prototype.setTime = function(time) {
	this.m_time = time;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalCoreTrack
*/
Cal3D.CalCoreTrack = function() {
	this.m_coreBoneId = -1;
	this.m_keyframes = [];
};

/**
	Create the core track.
	@returns {boolean} true if succeeded; false if not.
*/
Cal3D.CalCoreTrack.prototype.create = function() {
	return true;
};

/**
	Destroy the core track.
*/
Cal3D.CalCoreTrack.prototype.destroy = function() {
	// destroy all core key-frames
	for(var i=0; i<this.m_keyframes.length; i++) {
		this.m_keyframes[i].destroy();
	}

	this.m_keyframes = [];
	this.m_coreBoneId = -1;
};

/**
	Get a state.<br />
	This function returns the state (translation and rotation of the core bone) for the specified time and duration.
	@param {number} time The time in seconds at which the state should be returned.
	@param {Cal3D.CalVector} translation A vector as an output parameter that will be fiiled with specified state.
	@param {Cal3D.CalQuaternion} rotation A quaternion as an output parameter that will be fiiled with specified state.
	@returns {boolean} true if succeeded; false if any error happened.
*/
Cal3D.CalCoreTrack.prototype.getState = function(time, translation, rotation) {
	var coreKeyframeIdBefore, coreKeyframeIdAfter;

	// get the key-frame after the requested time
	coreKeyframeIdAfter = this.getUpperBound(time);

	// check if the time is after the last key-frame
	if(coreKeyframeIdAfter == this.m_keyframes.length) {
		// return the last key-frame state
		coreKeyframeIdAfter--;
		rotation.assign(this.m_keyframes[coreKeyframeIdAfter].getRotation());
		translation.assign(this.m_keyframes[coreKeyframeIdAfter].getTranslation());
		return true;
	}

	// check if the time is before the first key-frame
	if(coreKeyframeIdAfter == 0) {
		// return the first keyframe state
		rotation.assign(this.m_keyframes[coreKeyframeIdAfter].getRotation());
		translation.assign(this.m_keyframes[coreKeyframeIdAfter].getTranslation());
		return true;
	}

	// get the key-frame before the requested one
	coreKeyframeIdBefore = coreKeyframeIdAfter - 1;

	// get the two key-frames
	var coreKeyframeBefore, coreKeyframeAfter;
	coreKeyframeBefore = this.m_keyframes[coreKeyframeIdBefore];
	coreKeyframeAfter = this.m_keyframes[coreKeyframeIdAfter];

	// calculate the blending factor between the two key-frame states
	var blendFactor = (time - coreKeyframeBefore.getTime()) / (coreKeyframeAfter.getTime() - coreKeyframeBefore.getTime());

	/* 
		blend between the two key-frames
	*/
	translation.assign(coreKeyframeBefore.getTranslation());
	translation.blend(blendFactor, coreKeyframeAfter.getTranslation());

	rotation.assign(coreKeyframeBefore.getRotation());
	rotation.blend(blendFactor, coreKeyframeAfter.getRotation());

	return true;
};

/**
	Get the ID of the core bone.<br />
	This function returns the ID of the core bone to which the core track instance is attached to.
	@returns {number} The ID of the core bone.
*/
Cal3D.CalCoreTrack.prototype.getCoreBoneId = function() {
	return this.m_coreBoneId;
};

/**
	Set the ID of the core bone.<br />
	This function sets the ID of the core bone to which the core track instance is attached to.
	@param {number} coreBoneId  The ID of the core bone to which the core track should be attached to.
	@returns {boolean} true if succeeded; false if the core bone does not exist.
*/
Cal3D.CalCoreTrack.prototype.setCoreBoneId = function(coreBoneId) {
	if(coreBoneId < 0) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'coretrack.js');
		return false;
	}

	this.m_coreBoneId = coreBoneId;
	return true;
};

/**
	Get the number of core keyframes.<br />
	@returns {number} The number of core keyframes.
*/
Cal3D.CalCoreTrack.prototype.getCoreKeyframeCount = function() {
	return this.m_keyframes.length;
};

/**
	Get a core keyframe.<br />
	@param {number} index The index of the core keyframe.
	@returns {Cal3D.CalCoreKeyframe} The core keyframe.
*/
Cal3D.CalCoreTrack.prototype.getCoreKeyframe = function(index) {
	return this.m_keyframes[index];
};

/**
	Add a core keyframe.<br />
	This function adds a core keyframe to the core track instance.
	@param {Cal3D.CalCoreKeyframe} coreKeyframe The core keyframe to be added.
	@returns {boolean} ID of the added core keyframe.
*/
Cal3D.CalCoreTrack.prototype.addCoreKeyframe = function(coreKeyframe) {
	this.m_keyframes.push(coreKeyframe);

	var index = this.m_keyframes.length - 1;
	while(index > 0 && this.m_keyframes[index].getTime() < this.m_keyframes[index-1].getTime()) {
		var temp = this.m_keyframes[index];
		this.m_keyframes[index] = this.m_keyframes[index - 1];
		this.m_keyframes[index - 1] = temp;
		index--;
	}

	return true;
};

/**
	Remove a core keyframe from the core track.<br />
	This function removes a core keyframe from the core track instance.
	@param {number} index The index of the core keyframe to be removed.
*/
Cal3D.CalCoreTrack.prototype.removeCoreKeyFrame = function(index) {
	this.m_keyframes.splice(index, 1);
};

/**
	Scale the core track.<br />
	This function rescale all the data that are in the core track instance.
	@param {number} factor The scale factor.
*/
Cal3D.CalCoreTrack.prototype.scale = function(factor) {
	for(var keyframeId=0; keyframeId<this.m_keyframes.length; keyframeId++) {
		this.m_keyframes[keyframeId].getTranslation().multScalarLocal(factor);
	}
};

/**
	@private
*/
Cal3D.CalCoreTrack.prototype.getUpperBound = function(time) {
	var lower = 0;
	var upper = this.m_keyframes.length - 1;

	while(lower < upper - 1) {
		var middle = (lower + upper) >> 1;

		if(time >= this.m_keyframes[middle].getTime()) {
			lower = middle;
		}
		else {
			upper = middle;
		}
	}

	return upper;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalCoreAnimation
*/
Cal3D.CalCoreAnimation = function() {
	this.m_listCallbacks = [];
	this.m_duration = 0;
	this.m_listCoreTrack = [];
	this.m_name = '';
	this.m_filename = '';
};

/**
	Adds a core track.<br />
	This function adds a core track to the core animation instance.
	@param {Cal3D.CalCoreTrack} coreTrack The core track to be added.
	@returns {boolean} false if error happened.
*/
Cal3D.CalCoreAnimation.prototype.addCoreTrack = function(coreTrack) {
	this.m_listCoreTrack.push(coreTrack);
	return true;
};

/**
	Get a core track.<br />
	This function returns the core track for a given core bone ID.
	@param {number}  coreBoneId The core bone ID of the core track that should be returned.
	@returns {Cal3D.CalCoreTrack} The core track instance. null if the core track does not exist. 
*/
Cal3D.CalCoreAnimation.prototype.getCoreTrack = function(coreBoneId) {
	// loop through all core tracks
	for(var iCoreTrack=0; iCoreTrack<this.m_listCoreTrack.length; iCoreTrack++) {
		// get the core track
		var coreTrack = this.m_listCoreTrack[iCoreTrack];

		// check if we found the matching core bone
		if(coreTrack.getCoreBoneId() == coreBoneId)
			return coreTrack;
	}

	// no match found
	return null;
};

/**
	Get the number of core tracks for this core animation.<br />
	This function returns the number of core tracks used for this core animation.
	@returns {number} The number of core tracks.
*/
Cal3D.CalCoreAnimation.prototype.getDuration = function() {
	return this.m_duration;
};

/**
	Set the duration.<br />
	This function sets the duration of the core animation instance.
	@param {number} duration The duration in seconds to be set.
*/
Cal3D.CalCoreAnimation.prototype.setDuration = function(duration) {
	this.m_duration = duration;
};

/**
	Scale the core animation.<br />
	This function rescale all the skeleton data that are in the core animation instance.
	@param {number} factor The scale factor.
*/
Cal3D.CalCoreAnimation.prototype.scale = function(factor) {
	// scale all core tracks
	for(var iCoreTrack=0; iCoreTrack<this.m_listCoreTrack.length; iCoreTrack++) {
		this.m_listCoreTrack[iCoreTrack].scale(factor);
	}
};

/**
	Set the name of the file in which the core animation is stored, if any.<br />
	@param {string} filename The file name.
*/
Cal3D.CalCoreAnimation.prototype.setFilename = function(filename) {
	this.m_filename = filename;
};

/**
	Get the name of the file in which the core animation is stored, if any.<br />
	@returns {string} The file name. An empty should be returned if the animation is not stored ina file.
*/
Cal3D.CalCoreAnimation.prototype.getFilename = function() {
	return this.m_filename;
};

/**
	Set the symbolic name of the core animation.<br />
	@param {string} name A symbolic name.
*/
Cal3D.CalCoreAnimation.prototype.setName = function(name) {
	this.m_name = name;
};

/**
	Get the symbolic name the core animation.<br />
	@returns {string} The symbolic name; An empty string will be returned if the symbolic name is not set yet.
*/
Cal3D.CalCoreAnimation.prototype.getName = function() {
	return this.m_name;
};

/**
	Add a callback to the current list of callbacks for this core animation.<br />
	@param {object} callback An object that implements the Cal3D.CalAnimationCallback interface.
	@param {number} min_interval Minimum interval (in seconds) between callbacks. Specifying 0 means call every update().
*/
Cal3D.CalCoreAnimation.prototype.registerCallback = function(callback, min_interval) {
	var record = new Cal3D.CalCoreAnimation.CallbackRecord;
	record.callback = callback;
	record.min_interval = min_interval;
	this.m_listCallbacks.push(record);
};

/**
	Remove a callback from the current list of callbacks for this core animation.<br />
	@param {object} callback The callback object to be removed.
*/
Cal3D.CalCoreAnimation.prototype.removeCallback = function(callback) {
	for(var i=0; i<this.m_listCallbacks.length; i++) {
		if(this.m_listCallbacks[i].callback == callback) {
			this.m_listCallbacks.splice(i, 1);
			return;
		}
	}
};

/**
	@private
*/
Cal3D.CalCoreAnimation.prototype.getCallbackList = function() {
	return this.m_listCallbacks;
};

/**
	Get the number of core tracks for this core animation.<br />
	This function returns the number of core tracks used for this core animation.
	@returns {number} The number of core tracks.
*/
Cal3D.CalCoreAnimation.prototype.getTrackCount = function() {
	return this.m_listCoreTrack.length;
};

/**
	Get the core track list.<br />
	This function returns the list that contains all core tracks of the core animation instance.
	@returns {Array} The core track list.
*/
Cal3D.CalCoreAnimation.prototype.getListCoreTrack = function() {
	return this.m_listCoreTrack;
};

/**
	Get the total number of core keyframes used for this animation.<br />
	This function returns the total number of core keyframes used for this animation instance (i.e.: the sum of all core keyframes of all core tracks).
	@returns {number} The number of core keyframes.
*/
Cal3D.CalCoreAnimation.prototype.getTotalNumberOfKeyframes = function() {
	var nbKeys = 0;
	for(var iCoreTrack=0; iCoreTrack<this.m_listCoreTrack.length; iCoreTrack++) {
		var coreTrack = this.m_listCoreTrack[iCoreTrack];
		nbKeys += coreTrack.getCoreKeyframeCount();
	}

	return nbKeys;
};

/**
	@private
*/
Cal3D.CalCoreAnimation.CallbackRecord = function() {
	this.callback = null;
	this.min_interval = 0;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalSubmesh
*/
Cal3D.CalSubmesh = function(coreSubmesh) {
	this.m_coreSubmesh = coreSubmesh;
	this.m_vectorMorphTargetWeight = new Array(coreSubmesh.getCoreSubMorphTargetCount());
	this.m_vectorVertex = [];
	this.m_vectorNormal = [];
	this.m_vectorvectorTangentSpace = [];
	this.m_vectorFace = new Array(coreSubmesh.getFaceCount());
	this.m_vectorPhysicalProperty = [];
	this.m_vertexCount = 0;
	this.m_faceCount = 0;
	this.m_coreMaterialId = -1;
	this.m_bInternalData = false;

	// initialize faces
	for(var faceId=0; faceId<this.m_vectorFace.length; faceId++) {
		this.m_vectorFace[faceId] = new Cal3D.CalSubmesh.Face;
	}

	// set the initial lod level
	this.setLodLevel(1);

	// set the initial morph target weights
	var morphTargetCount = coreSubmesh.getCoreSubMorphTargetCount();
	for(var morphTargetId=0; morphTargetId<morphTargetCount; morphTargetId++) {
		this.m_vectorMorphTargetWeight[morphTargetId] = 0;
	}

	// check if the submesh instance must handle the vertex and normal data internally
	if(coreSubmesh.getSpringCount() > 0) {
		var vertexCount = coreSubmesh.getVertexCount();

		this.m_vectorVertex = new Array(vertexCount);
		this.m_vectorNormal = new Array(vertexCount);

		this.m_vectorvectorTangentSpace = new Array(coreSubmesh.getVectorVectorTangentSpace().length);

		this.m_vectorPhysicalProperty = new Array(vertexCount);

		// get the vertex vector of the core submesh
		var vectorVertex = coreSubmesh.getVectorVertex();

		// copy the data from the core submesh as default values
		for(var vertexId=0; vertexId<vertexCount; vertexId++) {
			// copy the vertex data
			this.m_vectorVertex[vertexId] = new Cal3D.CalVector(vectorVertex[vertexId].position);
			var physicalProperty = new Cal3D.CalSubmesh.PhysicalProperty;
			physicalProperty.position.assign(vectorVertex[vertexId].position);
			physicalProperty.positionOld.assign(vectorVertex[vertexId].position);
			this.m_vectorPhysicalProperty[vertexId] = physicalProperty;

			// copy the normal data
			this.m_vectorNormal[vertexId] = new Cal3D.CalVector(vectorVertex[vertexId].normal);
		}

		this.m_bInternalData = true;
	}
};

/**
	Get the core submesh.<br />
	This function returns the core submesh on which this submesh instance is based on.
	@returns {Cal3D.CalCoreSubmesh} The core submesh.
*/
Cal3D.CalSubmesh.prototype.getCoreSubmesh = function() {
	return this.m_coreSubmesh;
};

/**
	Get the core material ID.<br />
	This function returns the core material ID of the submesh instance.
	@returns {number} The ID of the core material.
*/
Cal3D.CalSubmesh.prototype.getCoreMaterialId = function() {
	return this.m_coreMaterialId;
};

/**
	Get the number of faces.<br />
	This function returns the number of faces in the submesh instance.
	@returns {number} The number of faces.
*/
Cal3D.CalSubmesh.prototype.getFaceCount = function() {
	return this.m_faceCount;
};

/**
	Get the face data.<br />
	This function returns the face data (vertex indices) of the submesh instance. The LOD setting of the submesh instance is taken into account.
	@param {Array} faceBuffer The user-provided buffer where the face data is written to. This is an output parameter.
	@returns {number} The number of faces written to the buffer.
*/
Cal3D.CalSubmesh.prototype.getFaces = function(faceBuffer) {
	// copy the face vector to the face buffer
	for(var i=0, j=0; i<this.m_faceCount; i++, j+=3) {
		var faceVertId = this.m_vectorFace[i].vertexId;
		faceBuffer[j    ] = faceVertId[0];
		faceBuffer[j + 1] = faceVertId[1];
		faceBuffer[j + 2] = faceVertId[2];
	}

	return this.m_faceCount;
};

/**
	Get the normal list.<br />
	This function returns the list that contains all normals of the submesh instance.
	@returns {Array} The normal list.
*/
Cal3D.CalSubmesh.prototype.getVectorNormal = function() {
	return this.m_vectorNormal;
};

/**
	Get the list of the tangent space lists.<br />
	This function returns the list that contains all tangent space bases of the submesh instance. This list contains another list 
	because there can be more than one texture map at each vertex.
	@returns {Array} The  list of the tangent space lists.
*/
Cal3D.CalSubmesh.prototype.getVectorVectorTangentSpace = function() {
	return this.m_vectorvectorTangentSpace;
};

/**
	Get the physical property list.<br />
	This function returns the list that contains all physical properties of the submesh instance.
	@returns {Array} The physical property list.
*/
Cal3D.CalSubmesh.prototype.getVectorPhysicalProperty = function() {
	return this.m_vectorPhysicalProperty;
};

/**
	Get the vertex list.<br />
	This function returns the list that contains all vertices of the submesh instance.
	@returns {Array} The vertex list.
*/
Cal3D.CalSubmesh.prototype.getVectorVertex = function() {
	return this.m_vectorVertex;
};

/**
	Get the number of vertices.<br />
	This function returns the number of vertices in the submesh instance.
	@returns {number} The number of vertices.
*/
Cal3D.CalSubmesh.prototype.getVertexCount = function() {
	return this.m_vertexCount;
};

/**
	Check if the submesh instance handles vertex data internally.<br />
	This function returns wheter the submesh instance handles vertex data internally.
	@returns {boolean} Whether the submesh instance handles vertex data internally.
*/
Cal3D.CalSubmesh.prototype.hasInternalData = function() {
	return this.m_bInternalData;
};

/**
	Disable internal data (and thus springs system).
*/
Cal3D.CalSubmesh.prototype.disableInternalData = function() {
	if(this.m_bInternalData) {
		this.m_vectorVertex = [];
		this.m_vectorNormal = [];
		this.m_vectorvectorTangentSpace = [];
		this.m_vectorPhysicalProperty = [];
		this.m_bInternalData = false;
	}
};

/**
	Set the ID of the core material.<br />
	This function sets the core material ID of the submesh instance.
	@param {number} coreMaterialId The core material ID to be set.
*/
Cal3D.CalSubmesh.prototype.setCoreMaterialId = function(coreMaterialId) {
	this.m_coreMaterialId = coreMaterialId;
};

/**
	Set the current LOD level.<br />
	This function sets the current LOD level of the submesh instance.
	@param {number} lodLevel The LOD level in the range [0.0, 1.0].
*/
Cal3D.CalSubmesh.prototype.setLodLevel = function(lodLevel) {
	// clamp the lod level to [0.0, 1.0]
	if(lodLevel < 0) 
		lodLevel = 0;
	if(lodLevel > 1) 
		lodLevel = 1;

	// get the lod count of the core submesh
	var lodCount = this.m_coreSubmesh.getLodCount();

	// calculate the target lod count
	lodCount = ~~((1 - lodLevel) * lodCount);

	// calculate the new number of vertices
	this.m_vertexCount = this.m_coreSubmesh.getVertexCount() - lodCount;

	// get face vector of the core submesh
	var vectorFace = this.m_coreSubmesh.getVectorFace();

	// get face vector of the core submesh
	var vectorVertex = this.m_coreSubmesh.getVectorVertex();

	// calculate the new number of faces
	this.m_faceCount = vectorFace.length;

	for(var vertexId=vectorVertex.length-1; vertexId>=this.m_vertexCount; vertexId--) {
		this.m_faceCount -= vectorVertex[vertexId].faceCollapseCount;
	}

	// fill the face vector with the collapsed vertex ids
	for(var faceId=0; faceId<this.m_faceCount; faceId++) {
		for(var vertexId=0; vertexId<3; vertexId++) {
			// get the vertex id
			var collapsedVertexId = vectorFace[faceId].vertexId[vertexId];

			// collapse the vertex id until it fits into the current lod level
			// FIXME: this may cause an infinit loop
			while(collapsedVertexId >= this.m_vertexCount) {
				collapsedVertexId = vectorVertex[collapsedVertexId].collapseId;
			}

			// store the collapse vertex id in the submesh face vector
			this.m_vectorFace[faceId].vertexId[vertexId] = collapsedVertexId;
		}
	}
};

/**
	Check if tangent vectors are enabled.<br />
	This function returns true if the submesh contains tangent vectors.
	@param {number} mapId The ID of the texture map.
	@returns {boolean} Whether the tangent vectors are enabled.
*/
Cal3D.CalSubmesh.prototype.isTangentsEnabled = function(mapId) {
	return this.m_coreSubmesh.isTangentsEnabled(mapId);
};

/**
	Enable (and calculate) or disable the storage of tangent spaces.<br />
	This function enables or disables the storage of tangent space bases.
	@param {number} mapId The ID of the texture map.
	@param {boolean} enabled Set to true to enable the tangent spaces, false to disable it.
	@returns {boolean} true if succeeded; false if any error happened.
*/
Cal3D.CalSubmesh.prototype.enableTangents = function(mapId, enabled) {
	if(!this.m_coreSubmesh.enableTangents(mapId, enabled))
		return false;

	if(!this.m_bInternalData)
		return true;

	if(!enabled) {
		this.m_vectorvectorTangentSpace[mapId] = [];
		return true;
	}

	this.m_vectorvectorTangentSpace[mapId] = new Array(this.m_coreSubmesh.getVertexCount());

	// get the tangent space vector of the core submesh
	var vectorTangentSpace = this.m_coreSubmesh.getVectorVectorTangentSpace()[mapId];

	// copy the data from the core submesh as default values
	var vertexCount = this.m_coreSubmesh.getVertexCount();
	for(var vertexId=0; vertexId<vertexCount; vertexId++) {
		// copy the tangent space data
		var tangentSpace = new Cal3D.CalSubmesh.TangentSpace();
		tangentSpace.tangent.assign(vectorTangentSpace[vertexId].tangent);
		tangentSpace.crossFactor = vectorTangentSpace[vertexId].crossFactor;
		this.m_vectorvectorTangentSpace[mapId][vertexId] = tangentSpace;
	}

	return true;
};

Cal3D.CalSubmesh.prototype.getVectorWeight = function() {
	throw 'not implemented error';
};

/**
	Set weight of a morph target with the given ID.<br />
	@param {number} blendId The morph target ID.
	@param {number} weight The weight to be set.
*/
Cal3D.CalSubmesh.prototype.setMorphTargetWeight = function(blendId, weight) {
	this.m_vectorMorphTargetWeight[blendId] = weight;
};

/**
	Get eight of a morph target with the given ID.<br />
	@param {number} blendId The morph target ID.
	@returns {number} The weight of the morph target.
*/
Cal3D.CalSubmesh.prototype.getMorphTargetWeight = function(blendId) {
	return this.m_vectorMorphTargetWeight[blendId];
};

/**
	Get weight of the base vertices.<br />
	@returns {number} The weight of the base vertices.
*/
Cal3D.CalSubmesh.prototype.getBaseWeight = function() {
	var baseWeight = 1;
	var morphTargetCount = this.getMorphTargetWeightCount();
	for(var morphTargetId=0; morphTargetId<morphTargetCount; morphTargetId++) {
		baseWeight -= this.m_vectorMorphTargetWeight[morphTargetId];
	}

	return baseWeight;
};

/**
	Get the number of weights.<br />
	This function returns the number of weights.
	@returns {number} The number of weights.
*/
Cal3D.CalSubmesh.prototype.getMorphTargetWeightCount = function() {
	return this.m_vectorMorphTargetWeight.length;
};

/**
	Get the morph target weight list.<br />
	This function returns the list that contains all weights for each morph target instance.
	@returns {Array} The morph target weight list.
*/
Cal3D.CalSubmesh.prototype.getVectorMorphTargetWeight = function() {
	return this.m_vectorMorphTargetWeight;
};



/**
	@class PhysicalProperty

	The physical property structure for the CalSubmesh class.
*/
Cal3D.CalSubmesh.PhysicalProperty = function() {
	this.position = new Cal3D.CalVector;
	this.positionOld = new Cal3D.CalVector;
	this.force = new Cal3D.CalVector;
};



/**
	@class TangentSpace

	The tangent space structure for the CalSubmesh class.
*/
Cal3D.CalSubmesh.TangentSpace = function() {
	this.tangent = new Cal3D.CalVector;;
	this.crossFactor = 0;
};



/**
	@class Face

	The face structure for the CalSubmesh class.
*/
Cal3D.CalSubmesh.Face = function() {
	this.vertexId = [0, 0, 0];
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalMesh
*/
Cal3D.CalMesh = function(coreMesh) {
	this.m_model = null;
	this.m_coreMesh = coreMesh;
	this.m_vectorSubmesh = []

	// clone the mesh structure of the core mesh
	var vectorCoreSubmesh = coreMesh.getVectorCoreSubmesh();

	// get the number of submeshes
	var submeshCount = vectorCoreSubmesh.length;

	// clone every core submesh
	for(var submeshId=0; submeshId<submeshCount; submeshId++) {
		this.m_vectorSubmesh.push(new Cal3D.CalSubmesh(vectorCoreSubmesh[submeshId]));
	}
};

/**
	Get the core mesh.<br />
	This function returns the core mesh on which this mesh instance is based on.
	@returns {Cal3D.CalCoreMesh} The core mesh.
*/
Cal3D.CalMesh.prototype.getCoreMesh = function() {
	return this.m_coreMesh;
};

/**
	Get a submesh.<br />
	This function returns the submesh with the given ID.
	@param {number} submeshId The ID of the submesh.
	@returns {Cal3D.CalSubmesh} The submesh; null if it does not exist. 
*/
Cal3D.CalMesh.prototype.getSubmesh = function(submeshId) {
	if(submeshId < 0 || submeshId >= this.m_vectorSubmesh.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'mesh.js');
		return null;
	}

	return this.m_vectorSubmesh[submeshId];
};

/**
	Get the number of submeshes.<br />
	This function returns the number of submeshes in the mesh instance.
	@returns {number} The number of submeshes.
*/
Cal3D.CalMesh.prototype.getSubmeshCount = function() {
	return this.m_vectorSubmesh.length;
};

/**
	Get the submesh list.<br />
	This function returns the list that contains all submeshes of the mesh instance.
	@returns {Array} The submesh list.
*/
Cal3D.CalMesh.prototype.getVectorSubmesh = function() {
	return this.m_vectorSubmesh;
};

/**
	Set the current LOD level.<br />
	This function sets the current LOD level of the mesh instance.
	@param {number} lodLevel The LOD level in the range [0.0, 1.0].
*/
Cal3D.CalMesh.prototype.setLodLevel = function(lodLevel) {
	// change lod level of each submesh
	for(var submeshId=0; submeshId<this.m_vectorSubmesh.length; submeshId++) {
		// set the lod level in the submesh
		this.m_vectorSubmesh[submeshId].setLodLevel(lodLevel);
	}
};

/**
	Set the material set.<br />
	This function sets the material set of the mesh instance.
	@param {number} setId The ID of the material set.
*/
Cal3D.CalMesh.prototype.setMaterialSet = function(setId) {
	// change material of each submesh
	for(var submeshId=0; submeshId<this.m_vectorSubmesh.length; submeshId++) {
		// get the core material thread id of the submesh
		var coreMaterialThreadId = this.m_vectorSubmesh[submeshId].getCoreSubmesh().getCoreMaterialThreadId();

		// get the core material id for the given set id in the material thread
		var coreMaterialId = this.m_model.getCoreModel().getCoreMaterialId(coreMaterialThreadId, setId);

		// set the new core material id in the submesh
		this.m_vectorSubmesh[submeshId].setCoreMaterialId(coreMaterialId);
	}
};

/**
	Set the model.<br />
	This function sets the model to which the mesh instance is attached to.
	@param {Cal3D.CalModel} model The model to which the mesh instance should be attached to.
*/
Cal3D.CalMesh.prototype.setModel = function(model) {
	this.m_model = model;
};

/**
	Disable internal data (and thus spring system).
*/
Cal3D.CalMesh.prototype.disableInternalData = function() {
	// disable internal data of each submesh
	for(var submeshId=0; submeshId<this.m_vectorSubmesh.length; submeshId++) {
		// disable internal data of the submesh
		this.m_vectorSubmesh[submeshId].disableInternalData();
	}
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalBone
*/
Cal3D.CalBone = function(coreBone) {
	this.m_coreBone = coreBone;
	this.m_skeleton = null;
	this.m_accumulatedWeight = 0;
	this.m_accumulatedWeightAbsolute = 0;
	this.m_translation = new Cal3D.CalVector;
	this.m_rotation = new Cal3D.CalQuaternion;
	this.m_translationAbsolute = new Cal3D.CalVector;
	this.m_rotationAbsolute = new Cal3D.CalQuaternion;
	this.m_translationBoneSpace = new Cal3D.CalVector;
	this.m_rotationBoneSpace = new Cal3D.CalQuaternion;
	this.m_transformMatrix = new Cal3D.CalMatrix;
	this.m_boundingBox = new Cal3D.CalBoundingBox;

	this.clearState();
};

/**
	Interpolate the current state to another state.<br />
	This function interpolates the current state (relative translation and rotation) of the bone instance to another state of a given weight.
	@param {number} weight The blending weight.
	@param {Cal3D.CalVector} translation The relative translation to be interpolated to.
	@param {Cal3D.CalQuaternion} rotation The relative rotation to be interpolated to.
*/
Cal3D.CalBone.prototype.blendState = function(weight, translation, rotation) {
	if(this.m_accumulatedWeightAbsolute == 0) {
		// it is the first state, so just copy it into the bone state
		this.m_translationAbsolute.assign(translation);
		this.m_rotationAbsolute.assign(rotation);
		this.m_accumulatedWeightAbsolute = weight;
	}
	else {
		// it is not the first state, so blend all attributes
		var factor = weight / (this.m_accumulatedWeightAbsolute + weight);

		this.m_translationAbsolute.blend(factor, translation);
		this.m_rotationAbsolute.blend(factor, rotation);
		this.m_accumulatedWeightAbsolute += weight;
	}
};

/**
	Calculate the current state.<br />
	This function calculates the current state (absolute translation and rotation, as well as the bone space transformation) of 
	the bone instance and all its children.
*/
Cal3D.CalBone.prototype.calculateState = function() {
	// check if the bone was not touched by any active animation
	if(this.m_accumulatedWeight == 0) {
		// set the bone to the initial skeleton state
		this.m_translation.assign(this.m_coreBone.getTranslation());
		this.m_rotation.assign(this.m_coreBone.getRotation());
	}

	// get parent bone id
	var parentId = this.m_coreBone.getParentId();

	if(parentId == -1) {
		// no parent, this means absolute state == relative state
		this.m_translationAbsolute.assign(this.m_translation);
		this.m_rotationAbsolute.assign(this.m_rotation);
	}
	else {
		// get the parent bone
		var parent = this.m_skeleton.getBone(parentId);

		// transform relative state with the absolute state of the parent
		this.m_translationAbsolute.assign(this.m_translation);
		this.m_translationAbsolute.multQuaternionLocal(parent.getRotationAbsolute());
		this.m_translationAbsolute.addLocal(parent.getTranslationAbsolute());

		this.m_rotationAbsolute.assign(this.m_rotation);
		this.m_rotationAbsolute.multQuaternionLocal(parent.getRotationAbsolute());
	}

	// calculate the bone space transformation
	this.m_translationBoneSpace.assign(this.m_coreBone.getTranslationBoneSpace());
	this.m_translationBoneSpace.multQuaternionLocal(this.m_rotationAbsolute);
	this.m_translationBoneSpace.addLocal(this.m_translationAbsolute);

	this.m_rotationBoneSpace.assign(this.m_coreBone.getRotationBoneSpace());
	this.m_rotationBoneSpace.multQuaternionLocal(this.m_rotationAbsolute);

	// Generate the vertex transform.  
	//TODO: If I ever add support for bone-scaling to Cal3D, this step will become significantly more complex.
	this.m_transformMatrix.assign(this.m_rotationBoneSpace);

	// calculate all child bones
	var listChildId = this.m_coreBone.getListChildId();
	for(var iChildId=0; iChildId<listChildId.length; iChildId++) {
		this.m_skeleton.getBone(listChildId[iChildId]).calculateState();
	}
};

/**
	Clear the current state.<br />
	This function clears the current state (absolute translation and rotation) of the bone instance and all its children.
*/
Cal3D.CalBone.prototype.clearState = function() {
	this.m_accumulatedWeight = 0;
	this.m_accumulatedWeightAbsolute = 0;
};

/**
	Get the core bone.<br />
	This function returns the core bone on which this bone instance is based on.
	@returns {Cal3D.CalCoreBone} The core bone instance. 
*/
Cal3D.CalBone.prototype.getCoreBone = function() {
	return this.m_coreBone;
};

/**
	Reset the bone to its core state.<br />
	This function changes the state of the bone to its default non-animated position and orientation. Child bones are unaffected and 
	may be animated independently.
*/
Cal3D.CalBone.prototype.setCoreState = function() {
	// set the bone to the initial skeleton state
	this.m_translation.assign(this.m_coreBone.getTranslation());
	this.m_rotation.assign(this.m_coreBone.getRotation());

	// set the appropriate weights
	this.m_accumulatedWeightAbsolute = 1;
	this.m_accumulatedWeight = 1;

	this.calculateState();
};

/**
	Reset the bone and children to core states.<br />
	This function changes the state of the bone to its default non-animated position and orientation. All child bones are also set in this manner.
*/
Cal3D.CalBone.prototype.setCoreStateRecursive = function() {
	// set the bone to the initial skeleton state
	this.m_translation.assign(this.m_coreBone.getTranslation());
	this.m_rotation.assign(this.m_coreBone.getRotation());

	// set the appropriate weights
	this.m_accumulatedWeightAbsolute = 1;
	this.m_accumulatedWeight = 1;

	// set core state for all child bones
	var listChildId = this.m_coreBone.getListChildId();
	for(var iChildId=0; iChildId<listChildId.length; iChildId++) {
		this.m_skeleton.getBone(listChildId[iChildId]).setCoreStateRecursive();
	}

	this.calculateState() ;
};

/**
	Set the current rotation.<br />
	This function sets the current relative rotation of the bone instance.
	@param {Cal3D.CalQuaternion} rotation The rotation to be set.
*/
Cal3D.CalBone.prototype.setRotation = function(rotation) {
	this.m_rotation.assign(rotation);
	this.m_accumulatedWeightAbsolute = 1;
	this.m_accumulatedWeight = 1;
};

/**
	Get the current rotation.<br />
	This function returns the current relative rotation of the bone instance.
	@returns {Cal3D.CalQuaternion} The current rotation.
*/
Cal3D.CalBone.prototype.getRotation = function() {
	return this.m_rotation;
};

/**
	Get the current absolute rotation.<br />
	This function returns the current absolute rotation of the bone instance.
	@returns {Cal3D.CalQuaternion} The current absolute rotation.
*/
Cal3D.CalBone.prototype.getRotationAbsolute = function() {
	return this.m_rotationAbsolute;
};

/**
	Get the current bone space rotation.<br />
	This function returns the current rotation to bring a point into the bone instance space.
	@returns {Cal3D.CalQuaternion} The current bone space rotation.
*/
Cal3D.CalBone.prototype.getRotationBoneSpace = function() {
	return this.m_rotationBoneSpace;
};

/**
	Set the current translation.<br />
	This function sets the current relative translation of the bone instance.
	@param {Cal3D.CalVector} translation The translation to be set.
*/
Cal3D.CalBone.prototype.setTranslation = function(translation) {
	this.m_translation.assign(translation);
	this.m_accumulatedWeightAbsolute = 1;
	this.m_accumulatedWeight = 1;
};

/**
	Get the current translation.<br />
	This function returns the current relative translation of the bone instance.
	@returns {Cal3D.CalVector} The relative translation to the parent.
*/
Cal3D.CalBone.prototype.getTranslation = function() {
	return this.m_translation;
};

/**
	Get the current absolute translation.<br />
	This function returns the current absolute translation of the bone instance.
	@returns {Cal3D.CalVector} The absolute translation to the parent.
*/
Cal3D.CalBone.prototype.getTranslationAbsolute = function() {
	return this.m_translationAbsolute;
};

/**
	Get the current bone space translation.<br />
	This function returns the current translation to bring a point into the bone instance space.
	@returns {Cal3D.CalVector} The current bone space translation.
*/
Cal3D.CalBone.prototype.getTranslationBoneSpace = function() {
	return this.m_translationBoneSpace;
};

/**
	Get the current transform matrix.<br />
	This function returns the current transform matrix of the bone instance.
	@returns {Cal3D.CalMatrix} The current transform matrix.
*/
Cal3D.CalBone.prototype.getTransformMatrix = function() {
	return this.m_transformMatrix;
};

/**
	Lock the current state.<br />
	This function locks the current state (absolute translation and rotation) of the bone instance and all its children.
*/
Cal3D.CalBone.prototype.lockState = function() {
	// clamp accumulated weight
	if(this.m_accumulatedWeightAbsolute > 1 - this.m_accumulatedWeight) {
		this.m_accumulatedWeightAbsolute = 1 - this.m_accumulatedWeight;
	}

	if(this.m_accumulatedWeightAbsolute > 0) {
		if(this.m_accumulatedWeight == 0) {
			// it is the first state, so just copy it into the bone state
			this.m_translation.assign(this.m_translationAbsolute);
			this.m_rotation.assign(this.m_rotationAbsolute);
			this.m_accumulatedWeight = this.m_accumulatedWeightAbsolute;
		}
		else {
			// it is not the first state, so blend all attributes
			var factor = this.m_accumulatedWeightAbsolute / (this.m_accumulatedWeight + this.m_accumulatedWeightAbsolute);

			this.m_translation.blend(factor, this.m_translationAbsolute);
			this.m_rotation.blend(factor, this.m_rotationAbsolute);
			this.m_accumulatedWeight += this.m_accumulatedWeightAbsolute;
		}

		this.m_accumulatedWeightAbsolute = 0;
	}
};

/**
	Set the skeleton the bone is attached to.<br />
	This function sets the skeleton to which the bone instance is attached to.
	@param {Cal3D.CalSkeleton} skeleton The skeleton to which the bone instance should be attached to.
*/
Cal3D.CalBone.prototype.setSkeleton = function(skeleton) {
	this.m_skeleton = skeleton;
};

/**
	Calculate the bounding box of the bone.<br />
	This function calculates the bounding box of the bone instance.
*/
Cal3D.CalBone.prototype.calculateBoundingBox = function() {
	if(!this.getCoreBone().isBoundingBoxPrecomputed())
		return;

	var dir = new Cal3D.CalVector(1, 0, 0);
	dir.multMatrixLocal(this.getTransformMatrix());
	this.m_boundingBox.plane[0].setNormal(dir);

	dir.assign(-1, 0, 0);
	dir.multMatrixLocal(this.getTransformMatrix());
	this.m_boundingBox.plane[1].setNormal(dir);

	dir.assign(0, 1, 0);
	dir.multMatrixLocal(this.getTransformMatrix());
	this.m_boundingBox.plane[2].setNormal(dir);

	dir.assign(0, -1, 0);
	dir.multMatrixLocal(this.getTransformMatrix());
	this.m_boundingBox.plane[3].setNormal(dir);

	dir.assign(0, 0, 1);
	dir.multMatrixLocal(this.getTransformMatrix());
	this.m_boundingBox.plane[4].setNormal(dir);

	dir.assign(0, 0, -1);
	dir.multMatrixLocal(this.getTransformMatrix());
	this.m_boundingBox.plane[5].setNormal(dir);

	var position = new Cal3D.CalVector;
	for(var i=0; i<6; i++) {
		this.getCoreBone().getBoundingData(i, position);

		position.multMatrixLocal(this.getTransformMatrix());
		position.addLocal(this.getTranslationBoneSpace());

		for(var planeId=0; planeId<6; planeId++) {
			if(this.m_boundingBox.plane[planeId].eval(position) < 0) {
				this.m_boundingBox.plane[planeId].setPosition(position);
			}
		}
	}
};

/**
	Get the current bounding box of the bone.<br />
	This function returns the current bounding box of the bone instance.
	@returns {Cal3D.CalBoundingBox} The bounding box of the bone.
*/
Cal3D.CalBone.prototype.getBoundingBox = function() {
	return this.m_boundingBox;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalSkeleton
*/
Cal3D.CalSkeleton = function(coreSkeleton) {
	this.m_coreSkeleton = coreSkeleton;
	this.m_vectorBone = [];
	this.m_isBoundingBoxesComputed = false;

	// clone the skeleton structure of the core skeleton
	var vectorCoreBone = coreSkeleton.getVectorCoreBone();

	// get the number of bones
	var boneCount = vectorCoreBone.length;

	// clone every core bone
	for(var boneId=0; boneId<boneCount; boneId++) {
		var bone = new Cal3D.CalBone(vectorCoreBone[boneId]);

		// set skeleton in the bone instance
		bone.setSkeleton(this);

		// insert bone into bone vector
		this.m_vectorBone.push(bone);
	}
};

/**
	Calculate the state of the skeleton.<br />
	This function calculates the state of the skeleton instance by recursively calculating the states of its bones.
*/
Cal3D.CalSkeleton.prototype.calculateState = function() {
	// calculate all bone states of the skeleton
	var listRootCoreBoneId = this.m_coreSkeleton.getVectorRootCoreBoneId();

	for(var iRootCoreBoneId=0; iRootCoreBoneId<listRootCoreBoneId.length; iRootCoreBoneId++) {
		this.m_vectorBone[ listRootCoreBoneId[iRootCoreBoneId] ].calculateState();
	}
	this.m_isBoundingBoxesComputed = false;
};

/**
	Clear the state of the skeleton.<br />
	This function clears the state of the skeleton instance by recursively clearing the states of its bones.
*/
Cal3D.CalSkeleton.prototype.clearState = function() {
	// clear all bone states of the skeleton
	for(var boneId=0; boneId<this.m_vectorBone.length; boneId++) {
		this.m_vectorBone[boneId].clearState();
	}
	this.m_isBoundingBoxesComputed = false;
};

Cal3D.CalSkeleton.prototype.create = function(coreSkeleton) {
	throw 'not implemented error';
};

/**
	Get a bone of the skeleton.<br />
	This function returns the bone with the given ID.
	@param {number} boneId The The ID of the bone.
	@returns {Cal3D.CalBone} The bone.
*/
Cal3D.CalSkeleton.prototype.getBone = function(boneId) {
	return this.m_vectorBone[boneId];
};

/**
	Get the core skeleton.<br />
	This function returns the core skeleton on which this skeleton instance is based on.
	@returns {Cal3D.CalCoreSkeleton} The core skeleton.
*/
Cal3D.CalSkeleton.prototype.getCoreSkeleton = function() {
	return this.m_coreSkeleton;
};

/**
	Get the bone list.<br />
	This function returns the list that contains all bones of the skeleton instance.
	@returns {Array} The bone list.
*/
Cal3D.CalSkeleton.prototype.getVectorBone = function() {
	return this.m_vectorBone;
};

/**
	Get the number of bones.<br />
	This function returns the number of bones of the skeleton instance.
	@returns {number} The number of bones.
*/
Cal3D.CalSkeleton.prototype.getBoneCount = function() {
	return this.m_vectorBone.length;
};

/**
	Lock the state of the skeleton.<br />
	This function locks the state of the skeleton instance by recursively locking the states of its bones.
*/
Cal3D.CalSkeleton.prototype.lockState = function() {
	// lock all bone states of the skeleton
	for(var boneId=0; boneId<this.m_vectorBone.length; boneId++) {
		this.m_vectorBone[boneId].lockState();
	}
};

/**
	Calculate axis aligned bounding box (AABB) of skeleton bones.<br />
	@param {Cal3D.CalVector} min (Optional) The vector where the min values of the bounding box are stored. This is an output parameter.
	@param {Cal3D.CalVector} max (Optional) The vector where the max values of the bounding box are stored. This is an output parameter.
	@returns {object} An object holding the min and max values of the bounding box.
*/
Cal3D.CalSkeleton.prototype.getBoneBoundingBox = function(min, max) {
	if(!min)
		min = new Cal3D.CalVector;
	if(!max)
		max = new Cal3D.CalVector;

	if(!this.m_isBoundingBoxesComputed) {
		this.calculateBoundingBoxes();
	}

	var boneId = 0;
	if(this.m_vectorBone.length > 0) {
		var translation = this.m_vectorBone[boneId].getTranslationAbsolute();

		min.assign(translation);
		max.assign(translation);

		boneId++;
	}

	for(; boneId<this.m_vectorBone.length; boneId++) {
		var translation = this.m_vectorBone[boneId].getTranslationAbsolute();

		if(translation.x > max.x)
			max.x = translation.x;
		else if(translation.x < min.x)
			min.x = translation.x;

		if(translation.y > max.y)
			max.y = translation.y;
		else if(translation.y < min.y)
			min.y = translation.y;

		if(translation.z > max.z)
			max.z = translation.z;
		else if(translation.z < min.z)
			min.z = translation.z;
	}

	return { min: min, max: max };
};

/**
	Calculate bones' bounding boxes.<br />
	This function calculates the bounding box of every bone in the Skeleton.
*/
Cal3D.CalSkeleton.prototype.calculateBoundingBoxes = function() {
	if(this.m_isBoundingBoxesComputed) 
		return;

	for(var boneId=0; boneId<this.m_vectorBone.length; boneId++) { 
		this.m_vectorBone[boneId].calculateBoundingBox();
	}
	this.m_isBoundingBoxesComputed = true;
};

/**
	Get transformed bone points of the skeleton. This is a debugging function.
	@param {Array} pointBuffer The user-provided buffer where the transformed bone point data is written to. This is an output parameter.
	@returns {number} The number of bone points written to the buffer.
*/
Cal3D.CalSkeleton.prototype.getBonePoints = function(pointBuffer) {
	var boneCount = this.m_vectorBone.length;

	var p = 0;
	for(var boneId=0; boneId<boneCount; boneId++) {
		var translation = this.m_vectorBone[boneId].getTranslationAbsolute();

		pointBuffer[p    ] = translation.x;
		pointBuffer[p + 1] = translation.y;
		pointBuffer[p + 2] = translation.z;

		p += 3;
	}

	return boneCount;
};

/**
	Get bone points (without transformation) of the skeleton. This is a debugging function.
	@param {Array} pointBuffer The user-provided buffer where the bone point data is written to. This is an output parameter.
	@returns {number} The number of bone points written to the buffer.
*/
Cal3D.CalSkeleton.prototype.getBonePointsStatic = function(pointBuffer) {
	var boneCount = this.m_vectorBone.length;

	var p = 0;
	for(var boneId=0; boneId<boneCount; boneId++) {
		var translation = this.m_vectorBone[boneId].getCoreBone().getTranslationAbsolute();

		pointBuffer[p    ] = translation.x;
		pointBuffer[p + 1] = translation.y;
		pointBuffer[p + 2] = translation.z;

		p += 3;
	}
	
	return boneCount;
};

/**
	Get transformed bone lines of the skeleton. This is a debugging function.
	@param {Array} pointBuffer The user-provided buffer where the transformed vertices of bone line data is written to. This is an output parameter.
	@returns {number} The number of vertices of bone lines written to the buffer.
*/
Cal3D.CalSkeleton.prototype.getBoneLines = function(lineBuffer) {
	var boneCount = this.m_vectorBone.length;
	
	var p = 0;
	var numLines = 0;
	for(var boneId=0; boneId<boneCount; boneId++) {
		var bone = this.m_vectorBone[boneId];

		var parentId = bone.getCoreBone().getParentId();
		if(parentId != -1) {
			var parent = this.m_vectorBone[parentId];

			var translation = bone.getTranslationAbsolute();
			var translationParent = parent.getTranslationAbsolute();

			lineBuffer[p    ] = translationParent.x;
			lineBuffer[p + 1] = translationParent.y;
			lineBuffer[p + 2] = translationParent.z;

			lineBuffer[p + 3] = translation.x;
			lineBuffer[p + 4] = translation.y;
			lineBuffer[p + 5] = translation.z;

			p += 6;
			numLines++;
		}
	}

	return numLines;
};

/**
	Get bone lines (without tranfromation) of the skeleton. This is a debugging function.
	@param {Array} pointBuffer The user-provided buffer where the vertices of bone line data is written to. This is an output parameter.
	@returns {number} The number of vertices of bone lines written to the buffer.
*/
Cal3D.CalSkeleton.prototype.getBoneLinesStatic = function(lineBuffer) {
	var boneCount = this.m_vectorBone.length;
	
	var p = 0;
	var numLines = 0;
	for(var boneId=0; boneId<boneCount; boneId++) {
		var bone = this.m_vectorBone[boneId];

		var parentId = bone.getCoreBone().getParentId();
		if(parentId != -1) {
			var parent = this.m_vectorBone[parentId];

			var translation = bone.getCoreBone().getTranslationAbsolute();
			var translationParent = parent.getCoreBone().getTranslationAbsolute();

			lineBuffer[p    ] = translationParent.x;
			lineBuffer[p + 1] = translationParent.y;
			lineBuffer[p + 2] = translationParent.z;

			lineBuffer[p + 3] = translation.x;
			lineBuffer[p + 4] = translation.y;
			lineBuffer[p + 5] = translation.z;

			p += 6;
			numLines++;
		}
	}

	return numLines;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalModel
*/
Cal3D.CalModel = function(coreModel) {
	this.m_coreModel = coreModel;
	this.m_skeleton = new Cal3D.CalSkeleton(coreModel.getCoreSkeleton());
	this.m_mixer = new Cal3D.CalMixer(this);
	this.m_morphTargetMixer = new Cal3D.CalMorphTargetMixer(this);
	this.m_physique = new Cal3D.CalPhysique(this);
	this.m_springSystem = new Cal3D.CalSpringSystem(this);
	this.m_renderer = new Cal3D.CalRenderer(this);
	this.m_userData = null;
	this.m_vectorMesh = [];
	this.m_boundingBox = new Cal3D.CalBoundingBox;
};

/**
	Attach a mesh to the model.<br />
	This function attachs a mesh to the model instance.
	@param {number} coreMeshId The ID of the mesh to be attached.
	@returns {boolean} true if succeeded; false if the mesh does not exist.
*/
Cal3D.CalModel.prototype.attachMesh = function(coreMeshId) {
	// check if the id is valid
	if(coreMeshId < 0 || coreMeshId >= this.m_coreModel.getCoreMeshCount()) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'model.js');
		return false;
	}

	// get the core mesh
	var coreMesh = this.m_coreModel.getCoreMesh(coreMeshId);

	// check if the mesh is already attached
	for(var meshId=0; meshId<this.m_vectorMesh.length; meshId++) {
		// check if we found the matching mesh
		if(this.m_vectorMesh[meshId].getCoreMesh() == coreMesh) {
			// mesh is already active so nothing to do
			return true;
		}
	}

	// create a new mesh instance
	var mesh = new Cal3D.CalMesh(coreMesh);

	// set model in the mesh instance
	mesh.setModel(this);

	// insert the new mesh into the active list
	this.m_vectorMesh.push(mesh);

	return true;
};

/**
	Detach a mesh from the model.<br />
	This function detaches a mesh from the model instance.
	@param {number} coreMeshId The ID of the mesh to be detached.
	@returns {boolean} true if succeeded; false if the mesh does not exist or any error happened.
*/
Cal3D.CalModel.prototype.detachMesh = function(coreMeshId) {
	// check if the id is valid
	if(coreMeshId < 0 || coreMeshId >= this.m_coreModel.getCoreMeshCount()) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'model.js');
		return false;
	}

	// get the core mesh
	var coreMesh = this.m_coreModel.getCoreMesh(coreMeshId);

	// find the mesh for the given id
	for(var meshId=0; meshId<this.m_vectorMesh.length; meshId++) {
		// get the mesh
		var mesh = this.m_vectorMesh[meshId];

		// check if we found the matching mesh
		if(mesh.getCoreMesh() == coreMesh) {
			// erase the mesh out of the active mesh list
			this.m_vectorMesh.splice(meshId, 1);

			return true;
		}
	}

	return false;
};

/**
	Get the core model.<br />
	This function returns the core model on which this model instance is based on.
	@returns {Cal3D.CalCoreModel} The core model.
*/
Cal3D.CalModel.prototype.getCoreModel = function() {
	return this.m_coreModel;
};

/**
	Get an attached mesh.<br />
	This function returns the attached mesh with the given core mesh ID.
	@param {number} coreMeshId The core mesh ID of the mesh.
	@returns {Cal3D.CalMesh} The mesh; null if the mesh does not exist.
*/
Cal3D.CalModel.prototype.getMesh = function(coreMeshId) {
	// check if the id is valid
	if(coreMeshId < 0 || coreMeshId >= this.m_coreModel.getCoreMeshCount()) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'model.js');
		return null;
	}

	// get the core mesh
	var coreMesh = this.m_coreModel.getCoreMesh(coreMeshId);

	// search the mesh
	for(var meshId=0; meshId<this.m_vectorMesh.length; meshId++) {
		// check if we found the matching mesh
		if(this.m_vectorMesh[meshId].getCoreMesh() == coreMesh) {
			return this.m_vectorMesh[meshId];
		}
	}

	return null;
};

/**
	Get the mixer.<br />
	If a mixer that is not an instance of Cal3D.CalMixer was set with the CalModel.setAbstractMixer() method, an INVALID_MIXER_TYPE error 
	is set and null is returned.
	@returns {Cal3D.CalMixer} The mixer; null if the mixer is not set yet or not an instance of Cal3D.CalMixer.
*/
Cal3D.CalModel.prototype.getMixer = function() {
	if(!this.m_mixer)
		return null;

	if(!this.m_mixer.isDefaultMixer()) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_MIXER_TYPE, 'model.js');
		return null;
	}

	return this.m_mixer;
};

/**
	Get the mixer.<br />
	@returns {Cal3D.CalAbstractMixer} The mixer, null if the mixer is not set yet.
*/
Cal3D.CalModel.prototype.getAbstractMixer = function() {
	return this.m_mixer;
};

/**
	Set the mixer to a Cal3D.CalAbstractMixer subclass instance.<br />
	@param {Cal3D.CalAbstractMixer} mixer The mixer.
*/
Cal3D.CalModel.prototype.setAbstractMixer = function(mixer) {
	this.m_mixer = mixer;
};

/**
	Get the morph target mixer.<br />
	This function returns the morph target mixer.
	@returns {Cal3D.CalMorphTargetMixer} The morph target mixer.
*/
Cal3D.CalModel.prototype.getMorphTargetMixer = function() {
	return this.m_morphTargetMixer;
};

/**
	Get the physique.<br />
	This function returns the physique.
	@returns {Cal3D.CalPhysique} The physique.
*/
Cal3D.CalModel.prototype.getPhysique = function() {
	return this.m_physique;
};

/**
	Get the renderer.<br />
	This function returns the renderer.
	@returns {Cal3D.CalRenderer} The renderer.
*/
Cal3D.CalModel.prototype.getRenderer = function() {
	return this.m_renderer;
};

/**
	Get the skeleton.<br />
	This function returns the skeleton.
	@returns {Cal3D.CalSkeleton} The skeleton.
*/
Cal3D.CalModel.prototype.getSkeleton = function() {
	return this.m_skeleton;
};

/**
	Get spring system.<br />
	This function returns the spring system.
	@returns {Cal3D.CalSpringSystem} The spring system.
*/
Cal3D.CalModel.prototype.getSpringSystem = function() {
	return this.m_springSystem;
};

/**
	Get the global bounding box of the model.<br />
	This function returns the global bounding box of the model.
	@param {boolean} precision indicate if the function need to calculate a correct bounding box.
	@returns {Cal3D.CalBoundingBox} The bounding box of the model.
*/
Cal3D.CalModel.prototype.getBoundingBox = function(precision) {
	var norm = new Cal3D.CalVector(1, 0, 0);	
	this.m_boundingBox.plane[0].setNormal(norm);

	norm.assign(-1, 0, 0);	
	this.m_boundingBox.plane[1].setNormal(norm);

	norm.assign(0, 1, 0);	
	this.m_boundingBox.plane[2].setNormal(norm);

	norm.assign(0, -1, 0);	
	this.m_boundingBox.plane[3].setNormal(norm);

	norm.assign(0, 0, 1);	
	this.m_boundingBox.plane[4].setNormal(norm);

	norm.assign(0, 0, -1);	
	this.m_boundingBox.plane[5].setNormal(norm);

	if(precision)
		this.m_skeleton.calculateBoundingBoxes();

	var vectorBone = this.m_skeleton.getVectorBone();
	var bboxPoints = [  new Cal3D.CalVector, new Cal3D.CalVector, new Cal3D.CalVector, new Cal3D.CalVector, 
						new Cal3D.CalVector, new Cal3D.CalVector, new Cal3D.CalVector, new Cal3D.CalVector ];
	for(var boneId=0; boneId<vectorBone.length; boneId++) {
		var bone = vectorBone[boneId];

		// If it's just an approximation that are needed then
		// we just compute the bounding box from the skeleton
		if(!precision || !bone.getCoreBone().isBoundingBoxPrecomputed()) {
			var translation =  bone.getTranslationAbsolute();

			for(var planeId=0; planeId<6; planeId++) {
				if(this.m_boundingBox.plane[planeId].eval(translation) < 0) {
					this.m_boundingBox.plane[planeId].setPosition(translation);
				}
			}
		}
		else {
			var localBoundingBox = bone.getBoundingBox();
			localBoundingBox.computePoints(bboxPoints);

			for(var i=0; i<8; i++) {
				for(var planeId=0; planeId<6; planeId++) {
					if(this.m_boundingBox.plane[planeId].eval(bboxPoints[i]) < 0) {
						this.m_boundingBox.plane[planeId].setPosition(bboxPoints[i]);
					}
				}
			}				
		}
	}

	return this.m_boundingBox;
};

/**
	Get the user data.<br />
	This function returns the user data stored in the model instance.
	@returns {object} The user data stored in the model.
*/
Cal3D.CalModel.prototype.getUserData = function() {
	return this.m_userData
};

/**
	Get the mesh list.<br />
	This function returns the vector that contains all attached meshes of the model instance.
	@returns {Array} The mesh list. 
*/
Cal3D.CalModel.prototype.getVectorMesh = function() {
	return this.m_vectorMesh;
};

/**
	Set the current LOD level.<br />
	This function sets the current LOD level of all attached meshes.
	@param {number} lodLevel The LOD level in the range [0.0, 1.0].
*/
Cal3D.CalModel.prototype.setLodLevel = function(lodLevel) {
	// set the lod level in all meshes
	for(var meshId=0; meshId<this.m_vectorMesh.length; meshId++) {
		// set the lod level in the mesh
		this.m_vectorMesh[meshId].setLodLevel(lodLevel);
	}
};

/**
	Set the material set.<br />
	This function sets the material set of all attached meshes.
	@param {number} setId The ID of the material set.
*/
Cal3D.CalModel.prototype.setMaterialSet = function(setId) {
	// set the material set in all meshes
	for(var meshId=0; meshId<this.m_vectorMesh.length; meshId++) {
		// set the material set in the mesh
		this.m_vectorMesh[meshId].setMaterialSet(setId);
	}
};

/**
	Store user data.<br />
	This function stores user data in the model instance.
	@param {objest} userData The user data to be stored.
*/
Cal3D.CalModel.prototype.setUserData = function(userData) {
	this.m_userData = userData;
};

/**
	Update the model.<br />
	This function updates the model instance for a given amount of time.
	@param {number} deltaTime The elapsed time in seconds since the last update.
*/
Cal3D.CalModel.prototype.update = function(deltaTime) {
	this.m_mixer.updateAnimation(deltaTime);
	this.m_mixer.updateSkeleton();

	this.m_morphTargetMixer.update(deltaTime);
	this.m_physique.update();
	this.m_springSystem.update(deltaTime);
};

/**
	Disable internal data (and thus the springs system).
*/
Cal3D.CalModel.prototype.disableInternalData = function() {
	// disable internal data in all meshes
	for(var meshId=0; meshId<this.m_vectorMesh.length; meshId++) {
		// disable internal data in the mesh
		this.m_vectorMesh[meshId].disableInternalData();
	}
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalAnimation
*/
Cal3D.CalAnimation = function(coreAnimation) {
	this.m_coreAnimation = coreAnimation;
	this.m_lastCallbackTimes = [];
	this.m_type = Cal3D.CalAnimation.Type.TYPE_NONE;
	this.m_state = Cal3D.CalAnimation.State.STATE_NONE;
	this.m_time = 0;
	this.m_timeFactor = 1;
	this.m_weight = 0;

	// build up the last called list
	var callbackList = coreAnimation.getCallbackList();
	for(var i=0; i<callbackList.length; i++) {
		this.m_lastCallbackTimes.push(0);
	}
};

/**
	The animation type enumerations.
*/
Cal3D.CalAnimation.Type = {
	TYPE_NONE:		0, 
	TYPE_CYCLE:		1, 
	TYPE_POSE:		2, 
	TYPE_ACTION:	3
};

/**
	The animation state enumerations.
*/
Cal3D.CalAnimation.State = {
	STATE_NONE:		0, 
	STATE_SYNC:		1, 
	STATE_ASYNC:	2, 
	STATE_IN:		3, 
	STATE_STEADY:	4, 
	STATE_OUT:		5, 
	STATE_STOPPED:	6
};

/**
	Get the core animation.<br />
	This function returns the core animation on which this animation instance is based on.
	@returns {Cal3D.CalCoreAnimation} the core animation.
*/
Cal3D.CalAnimation.prototype.getCoreAnimation = function() {
	return this.m_coreAnimation;
};

/**
	Get animation state.<br />
	This function returns the state of the animation instance.
	@returns {number} animation state. The return value should be one of following state:
		<li><b>Cal3D.CalAnimation.State.STATE_NONE</b></li>
		<li><b>Cal3D.CalAnimation.State.STATE_SYNC</b></li>
		<li><b>Cal3D.CalAnimation.State.STATE_ASYNC</b></li>
		<li><b>Cal3D.CalAnimation.State.STATE_IN</b></li>
		<li><b>Cal3D.CalAnimation.State.STATE_STEADY</b></li>
		<li><b>Cal3D.CalAnimation.State.STATE_OUT</b></li>
		<li><b>Cal3D.CalAnimation.State.STATE_STOPPED</b></li>
*/
Cal3D.CalAnimation.prototype.getState = function() {
	return this.m_state;
};

/**
	Get the time.<br />
	This function returns the time of the animation instance.
	@returns {number} time in seconds.
*/
Cal3D.CalAnimation.prototype.getTime = function() {
	return this.m_time;
};

/**
	Get animation type.<br />
	This function returns the type of the animation instance.
	@returns {number} animation type. The return value should be one of the following type:
		<li><b>Cal3D.CalAnimation.Type.TYPE_NONE</b></li>
		<li><b>Cal3D.CalAnimation.Type.TYPE_CYCLE</b></li>
		<li><b>Cal3D.CalAnimation.Type.TYPE_POSE</b></li>
		<li><b>Cal3D.CalAnimation.Type.TYPE_ACTION</b></li>
*/
Cal3D.CalAnimation.prototype.getType = function() {
	return this.m_type;
};

/**
	Get the weight.<br />
	This function returns the weight of the animation instance.
	@returns {number} the weight.
*/
Cal3D.CalAnimation.prototype.getWeight = function() {
	return this.m_weight;
};

/**
	Set the time.<br />
	This function set the time of the animation instance.
	@param {number} time time in seconds.
*/
Cal3D.CalAnimation.prototype.setTime = function(time) {
	this.m_time = time;
};

/**
	Set the time factor.<br />
	This function sets the time factor of the animation instance. The time factor affect only sync animation
	@param {number} timeFactor time factor.
*/
Cal3D.CalAnimation.prototype.setTimeFactor = function(timeFactor) {
	this.m_timeFactor = timeFactor;
};

/**
	Get the time factor.<br />
	This function return the time factor of the animation instance.
	@returns {number} the time factor.
*/
Cal3D.CalAnimation.prototype.getTimeFactor = function() {
	return this.m_timeFactor;
};

/**
	@private
*/
Cal3D.CalAnimation.prototype.checkCallbacks = function(animationTime, model) {
	var callbackList = this.m_coreAnimation.getCallbackList();
	for (var i=0; i<callbackList.length; i++) {
		// support for dynamic adding of callbacks
		if (this.m_lastCallbackTimes.length <= i)
			this.m_lastCallbackTimes.push(animationTime);

		callbackList[i].callback.AnimationUpdate(animationTime, model, model.getUserData());
		if (animationTime > 0 && animationTime < this.m_lastCallbackTimes[i])		// looped
			this.m_lastCallbackTimes[i] -= this.m_coreAnimation.getDuration();
		else if (animationTime < 0 && animationTime > this.m_lastCallbackTimes[i])	// reverse-looped  
			this.m_lastCallbackTimes[i] += this.m_coreAnimation.getDuration();

		if ( (animationTime >= 0 && animationTime >= this.m_lastCallbackTimes[i] + callbackList[i].min_interval) ||
			 (animationTime <  0 && animationTime <= this.m_lastCallbackTimes[i] - callbackList[i].min_interval) ) {
			callbackList[i].callback.AnimationUpdate(animationTime, model, model.getUserData());
			this.m_lastCallbackTimes[i] = animationTime;
		}
	}
};

/**
	@private
*/
Cal3D.CalAnimation.prototype.completeCallbacks = function(model) {
	var callbackList = this.m_coreAnimation.getCallbackList();
	for(var i=0; i<callbackList.length; i++) {
		callbackList[i].callback.AnimationComplete(model, model.getUserData());
	}
};

/**
	@private
*/
Cal3D.CalAnimation.prototype.setType = function(type) {
	this.m_type = type;
};

/**
	@private
*/
Cal3D.CalAnimation.prototype.setState = function(state) {
	this.m_state = state;
};

/**
	@private
*/
Cal3D.CalAnimation.prototype.setWeight = function(weight) {
	this.m_weight = weight;
};



/**
	@class CalAnimationCallback

	The animation call-back interface.
*/
Cal3D.CalAnimationCallback = function() {
};

Cal3D.CalAnimationCallback.prototype.AnimationUpdate = function(anim_time, model, userData) {
};

Cal3D.CalAnimationCallback.prototype.AnimationComplete = function(model, userData) {
};



/**
	@class CalAnimationAction
*/
Cal3D.CalAnimationAction = function(coreAnimation) {
	// call constructor of super class
	Cal3D.CalAnimation.call(this, coreAnimation);

	this.m_delayIn = 0;
	this.m_delayOut = 0;
	this.m_delayTarget = 0;
	this.m_weightTarget = 0;
	this.m_autoLock = false;

	this.setType(Cal3D.CalAnimation.Type.TYPE_ACTION);
};

// inherited from CalAnimation
Cal3D.CalAnimationAction.prototype = new Cal3D.CalAnimation(new Cal3D.CalCoreAnimation /* dummy object */);

/**
	Execute the animation action instance.<br />
	This function executes the animation action instance.
	@param {number} delayIn The time in seconds until the animation action instance reaches the full weight from the beginning of its execution.
	@param {number} delayOut The time in seconds in which the animation action instance reaches zero weight at the end of its execution.
	@param {number} weightTarget Target weight.
	@param {boolean} autoLock If set to true, the animation will not be reset and removed on the last keyframe.
*/
Cal3D.CalAnimationAction.prototype.execute = function(delayIn, delayOut, weightTarget, autoLock) {
	if(weightTarget == undefined)
		weightTarget = 1;
	if(autoLock == undefined)
		autoLock = false;

	this.setState(Cal3D.CalAnimation.State.STATE_IN);
	this.setWeight(0);
	this.m_delayIn = delayIn;
	this.m_delayOut = delayOut;
	this.setTime(0);
	this.m_weightTarget = weightTarget;
	this.m_autoLock = autoLock;

	return true;
};

/**
	Update the animation action instance.<br />
	This function updates the animation action instance for a given amount of time.
	@param {number} deltaTime The elapsed time in seconds since the last update.
	@returns {boolean} Whether or not the end the the action has been reached. 
		<li>true, if the animation action instance is still active;</li>
		<li>false, if the execution of the animation action instance has ended.</li>
*/
Cal3D.CalAnimationAction.prototype.update = function(deltaTime) {
	// update animation action time
	if(this.getState() != Cal3D.CalAnimation.State.STATE_STOPPED) {
		this.setTime(this.getTime() + deltaTime * this.getTimeFactor());
	}

	// handle IN phase
	if(this.getState() == Cal3D.CalAnimation.State.STATE_IN) {
		// check if we are still in the IN phase
		if(this.getTime() < this.m_delayIn) {
			this.setWeight(this.getTime() / this.m_delayIn * this.m_weightTarget);
		}
		else {
			this.setState(Cal3D.CalAnimation.State.STATE_STEADY);
			this.setWeight(this.m_weightTarget);
		}
	}

	// handle STEADY
	if(this.getState() == Cal3D.CalAnimation.State.STATE_STEADY) {
		// check if we reached OUT phase
		if(!this.m_autoLock && this.getTime() >= this.getCoreAnimation().getDuration() - this.m_delayOut) {
			this.setState(Cal3D.CalAnimation.State.STATE_OUT);
		}
		// if the anim is supposed to stay locked on last keyframe, reset the time here.
		else if(this.m_autoLock && this.getTime() > this.getCoreAnimation().getDuration()) {
			this.setState(Cal3D.CalAnimation.State.STATE_STOPPED);
			this.setTime(this.getCoreAnimation().getDuration());
		}      
	}

	// handle OUT phase
	if(this.getState() == Cal3D.CalAnimation.State.STATE_OUT) {
		// check if we are still in the OUT phase
		if(this.getTime() < this.getCoreAnimation().getDuration()) {
			this.setWeight((this.getCoreAnimation().getDuration() - this.getTime()) / this.m_delayOut * this.m_weightTarget);
		}
		else {
			// we reached the end of the action animation
			this.setWeight(0);
			return false;
		}
	}

	return true;
};



/**
	@class CalAnimationCycle
*/
Cal3D.CalAnimationCycle = function(coreAnimation) {
	// call constructor of super class
	Cal3D.CalAnimation.call(this, coreAnimation);

	this.m_targetDelay = 0;
	this.m_targetWeight = 0;

	this.setType(Cal3D.CalAnimation.Type.TYPE_CYCLE);
	this.setState(Cal3D.CalAnimation.State.STATE_SYNC);

	this.setWeight(0);
};

// inherited from CalAnimation
Cal3D.CalAnimationCycle.prototype = new Cal3D.CalAnimation(new Cal3D.CalCoreAnimation /* dummy object */);

/**
	Interpolate the weight of the animation cycle instance.<br />
	This function interpolates the weight of the animation cycle instance to a new value in a given amount of time.
	@param {number} weight The weight to interpolate the animation cycle instance to. 
	@param {number} delay The time in seconds until the new weight should be reached. 
	@returns {boolean} true if succeeded; false if error happened.
*/
Cal3D.CalAnimationCycle.prototype.blend = function(weight, delay) {
  this.m_targetWeight = weight;
  this.m_targetDelay = delay;

  return true;
};

/**
	Put the animation cycle instance into async state.<br />
	This function puts the animation cycle instance into async state, which means that it will end after the current running cycle.
	@param {number} time The time in seconds at which the animation cycle instance was unlinked from the global mixer animation cycle.
	@param {number} duration The current duration of the global mixer animation cycle in seconds at the time of the unlinking. 
*/
Cal3D.CalAnimationCycle.prototype.setAsync = function(time, duration) {
	// check if thie animation cycle is already async
	if(this.getState() != Cal3D.CalAnimation.State.STATE_ASYNC) {
		if(duration == 0) {
			this.setTimeFactor(1);
			this.setTime(0);
		}
		else {
			this.setTimeFactor(this.getCoreAnimation().getDuration() / duration);
			this.setTime(time * this.getTimeFactor());
		}

		this.setState(Cal3D.CalAnimation.State.STATE_ASYNC);
	}
};

/**
	Update the animation cycle instance.<br />
	This function updates the animation cycle instance for a given amount of time.
	@param {number} deltaTime The elapsed time in seconds since the last update.
	@returns {boolean} Whether or not the animation cycle has ended.
		<li>true if the animation cycle instance is still active.</li>
		<li>false if the execution of the animation cycle instance has ended.</li>
*/
Cal3D.CalAnimationCycle.prototype.update = function(deltaTime) {
	if(this.m_targetDelay <= Math.abs(deltaTime)) {
		// we reached target delay, set to full weight
		this.setWeight(this.m_targetWeight);
		this.m_targetDelay = 0;

		// check if we reached the cycles end
		if(this.getWeight() == 0) {
			return false;
		}
	}
	else {
		// not reached target delay yet, interpolate between current and target weight
		var factor = deltaTime / this.m_targetDelay;
		this.setWeight((1 - factor) * this.getWeight() + factor * this.m_targetWeight);
		this.m_targetDelay -= deltaTime;
	}

	// update animation cycle time if it is in async state
	if(this.getState() == Cal3D.CalAnimation.State.STATE_ASYNC) {
		this.setTime(this.getTime() + deltaTime * this.getTimeFactor());
		if(this.getTime() >= this.getCoreAnimation().getDuration()) {
			this.setTime(this.getTime() % this.getCoreAnimation().getDuration());
		}
		if (this.getTime() < 0) {
			this.setTime(this.getTime() + this.getCoreAnimation().getDuration());
		}
	}

	return true;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalAbstractMixer

	CalAbstractMixer defines the API that CalModel relies on for blending and scheduling animations.<br />
	A third party mixer must implement this API in order to register itself with the CalModel.setAbstractMixer method. 
	The default mixer (CalMixer) is an example of such implementation.<br /><br />

	Cal3D expects a mixer to handle two tasks: scheduling and blending. Scheduling refers to everything related to time such 
	as when an animation must run or when it must stop. Blending defines how concurrent animations influence each other: for 
	instance walking and waving.<br /><br />

	If CalMixer proves to be insufficient for the applications needs, an alternate mixer can be implemented and used without 
	notifying Cal3D in any way. It is not mandatory to subclass CalAbstractMixer. However, when chosing this path, one must 
	also avoid using the CalModel.update method because it would use the default mixer instantiated by the CalModel.create method 
	with undesirable side effects. In addition libraries based on Cal3D (think NebulaDevice or OpenSceneGraph adapters) are 
	not aware of these constraints and will keep calling the CalModel.update method of CalModel regardless.<br /><br />

	Subclassing CalAbstractMixer when implementing an alternate mixer therefore provides a better integration with Cal3D and 
	libraries that rely on CalModel. However, an additional effort is required in order to achieve compatibility with libraries or 
	applications that rely on the CalMixer API (i.e. that use methods such as blendCycle or executeAction).  The CalMixer API is not 
	meant to be generic and there is no reason to define an abstract class that specifies it. For historical reasons and because 
	CalMixer is the default mixer, some applications and libraries (think Soya or CrystalSpace) depend on it. If they want to switch 
	to a scheduler with extended capabilities it might be painfull for them to learn a completely different API. A scheduler with the 
	ambition to obsolete CalMixer should therefore provide an API compatible with it to ease the migration process.<br /><br />

	Short summary, if you want to write a new mixer:<br /><br />

	1) An external mixer: ignore CalAbstractMixer and implement a mixer of your own. Avoid calling CalModel.update and any library or 
	application that will call it behind your back. Avoid libraries and applications that rely on the default mixer CalMixer, as returned 
	by CalModel.getMixer.<br /><br />

	2) A mixer registered in Cal3D: subclass CalAbstractMixer, register it with CalModel.setAbstractMixer.  Avoid libraries and 
	applications that rely on the default mixer CalMixer, as returned by CalModel.getMixer. CalModel.getMixer will return a null 
	pointer if CalModel.setAbstractMixer was called to set a mixer that is not an instance of CalMixer.<br /><br />

	3) A CalMixer replacement: same as 2) and provide a subclass of your own mixer that implements the CalMixer API so that existing 
	applications can switch to it by calling CalModel.getAbstractMixer instead of CalModel.getMixer. The existing code using the CalMixer 
	methods will keep working and the developper will be able to switch to a new API when convenient.
*/
Cal3D.CalAbstractMixer = function() {
};

/**
	See if the object is an instance of the default mixer (i.e. Cal3D.CalMixer).<br />
	@returns {boolean} true, if the object is an instance of Cal3D.CalMixer; false if not.
*/
Cal3D.CalAbstractMixer.prototype.isDefaultMixer = function() {
	return false;
};

/**
	Notify the instance that updateAnimation was last called eltaTime seconds ago. The internal scheduler of the instance 
	should terminate animations or update the timing information of active animations accordingly. It should not blend animations 
	together or otherwise modify the CalModel associated to these animations.<br />br />
	
	The CalModel.update method will call updateSkeleton immediately after updateAnimation if the instance was allocated by 
	CalModel.create (in which case it is a CalMixer instance) or if the instance was set via CalModel.setAbstractMixer.

	@param {number} deltaTime The elapsed time in seconds since the last call.
*/
Cal3D.CalAbstractMixer.prototype.updateAnimation = function(deltaTime) {
	throw 'abstract method error';
};

/**
	Update the skeleton of the corresponding CalModel (as provided to the create method) to match the current animation state (as 
	updated by the last call to updateAnimation).  The tracks of each active animation are blended to compute the position and 
	orientation of each bone of the skeleton. The updateAnimation method should be called just before calling updateSkeleton to 
	define the set of active animations.<br /><br />

	The CalModel.update method will call updateSkeleton immediately after updateAnimation if the instance was allocated by 
	CalModel.create (in which case it is a CalMixer instance) or if the instance was set via CalModel.setAbstractMixer.
*/
Cal3D.CalAbstractMixer.prototype.updateSkeleton = function() {
	throw 'abstract method error';
};



/**
	@class CalMixer
*/
Cal3D.CalMixer = function(model) {
	this.m_model = model;
	this.m_vectorAnimation = new Array(model.getCoreModel().getCoreAnimationCount());
	this.m_listAnimationAction = [];
	this.m_listAnimationCycle = [];
	this.m_animationTime = 0;
	this.m_animationDuration = 0;
	this.m_timeFactor = 1;
};

// inherited from CalAbstractMixer
Cal3D.CalMixer.prototype = new Cal3D.CalAbstractMixer;

/**
	See if the object is an instance of Cal3D.CalMixer.<br />
	@returns {boolean} Always return true. 
*/
Cal3D.CalMixer.prototype.isDefaultMixer = function() {
	return true;
};

/**
	Interpolate the weight of an animation cycle.<br />
	This function interpolates the weight of an animation cycle to a new value in a given amount of time. If the specified 
	animation cycle is not active yet, it is activated.
	@param {number} id The ID of the animation cycle to be blended.
	@param {number} weight The weight to interpolate the animation cycle to.
	@param {number} delay The time in seconds until the new weight should be reached.
	@returns {boolean} true if succeeded; false if any error happened.
*/
Cal3D.CalMixer.prototype.blendCycle = function(id, weight, delay) {
	if(id < 0 || id >= this.m_vectorAnimation.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'mixer.js');
		return false;
	}

	// get the animation for the given id
	var animation = this.m_vectorAnimation[id];

	// create a new animation instance if it is not active yet
	if(!animation) {
		// take the fast way out if we are trying to clear an inactive animation
		if(weight == 0) 
			return true;

		// get the core animation
		var coreAnimation = this.m_model.getCoreModel().getCoreAnimation(id);
		if(!coreAnimation) 
			return false;

		// ensure that the animation's first and last key frame match for proper looping
		Cal3D.addExtraKeyframeForLoopedAnim(coreAnimation);

		// create a new animation cycle instance
		var animationCycle = new Cal3D.CalAnimationCycle(coreAnimation);

		// insert the new animation into the tables
		this.m_vectorAnimation[id] = animationCycle;
		this.m_listAnimationCycle.unshift(animationCycle);

		// blend the animation
		return animationCycle.blend(weight, delay);
	}

	// check if this is really an animation cycle instance
	if(animation.getType() != Cal3D.CalAnimation.Type.TYPE_CYCLE) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_ANIMATION_TYPE, 'mixer.js');
		return false;
	}

	// clear the animation cycle from the active vector if the target weight is zero
	if(weight == 0) {
		this.m_vectorAnimation[id] = null;
	}

	// cast it to an animation cycle
	var animationCycle = animation;

	// blend the animation cycle
	animationCycle.blend(weight, delay);
	animationCycle.checkCallbacks(0, this.m_model);

	return true;
};

/**
	Fade an animation cycle out.<br />
	This function fades an animation cycle out in a given amount of time.
	@param {number} id The ID of the animation cycle to be faded out.
	@param {number} delay The time in seconds until the the animation cycle is completely removed.
	@returns {boolean} true if succeeded; false if any error happened.
*/
Cal3D.CalMixer.prototype.clearCycle = function(id, delay) {
	// check if the animation id is valid
	if(id < 0 || id >= this.m_vectorAnimation.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'mixer.js');
		return false;
	}

	// get the animation for the given id
	var animation = this.m_vectorAnimation[id];

	// we can only clear cycles that are active
	if(!animation) 
		return true;

	// check if this is really a animation cycle instance
	if(animation.getType() != Cal3D.CalAnimation.Type.TYPE_CYCLE) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_ANIMATION_TYPE, 'mixer.js');
		return false;
	}

	// clear the animation cycle from the active vector
	this.m_vectorAnimation[id] = null;

	// cast it to an animation cycle
	var animationCycle = animation;

	// set animation cycle to async state
	animationCycle.setAsync(this.m_animationTime, this.m_animationDuration);

	// blend the animation cycle
	animationCycle.blend(0, delay);
	animationCycle.checkCallbacks(0, this.m_model);

	return true;
};

/**
	Execute an animation action.<br />
	This function executes an animation action.
	@param {number} id The ID of the animation action to be execute.
	@param {number} delayIn  The time in seconds until the animation action reaches the full weight from the beginning of its execution.
	@param {number} delayOut The time in seconds in which the animation action reaches zero weight at the end of its execution.
	@param {number} weightTarget The weight to interpolate the animation action to.
	@param {boolean} autoLock This prevents the action from being reset and removed on the last keyframe if true.
	@returns {boolean} true if succeeded; false if any error happened.
*/
Cal3D.CalMixer.prototype.executeAction = function(id, delayIn, delayOut, weightTarget, autoLock) {
	// get the core animation
	var coreAnimation = this.m_model.getCoreModel().getCoreAnimation(id);
	if(!coreAnimation)
		return false;

	// create a new animation action instance
	var animationAction = new Cal3D.CalAnimationAction(coreAnimation);

	// insert the new animation into the table
	this.m_listAnimationAction.unshift(animationAction);

	// execute the animation
	animationAction.execute(delayIn, delayOut, weightTarget, autoLock);
	animationAction.checkCallbacks(0, this.m_model);

	return true;
};

/**
	Clear an active animation action.<br />
	This function removes an animation action from the blend list. This is particularly useful with auto-locked actions on their last frame.
	@param {number} id The ID of the animation action to be removed.
	@returns {boolean} true if succeeded; false if the animation does not exist any error happened.
*/
Cal3D.CalMixer.prototype.removeAction = function(id) {
	// get the core animation
	var coreAnimation = this.m_model.getCoreModel().getCoreAnimation(id);
	if(!coreAnimation)
		return false;

	// update all active animation actions of this model
	var animationActionId = 0;
	while(animationActionId < this.m_listAnimationAction.length) {
		// find the specified action and remove it
		if(this.m_listAnimationAction[animationActionId].getCoreAnimation() == coreAnimation) {
			// found, so remove
			this.m_listAnimationAction[animationActionId].completeCallbacks(this.m_model);
			this.m_listAnimationAction.splice(animationActionId, 1);
			return true;
		}

		animationActionId++;
	}

	return false;
};

/**
	Update all active animations.<br />
	This function updates all active animations of the mixer instance for a given amount of time.
	@param {number} deltaTime The elapsed time in seconds since the last update.
*/
Cal3D.CalMixer.prototype.updateAnimation = function(deltaTime) {
	// update the current animation time
	if(this.m_animationDuration == 0) {
		this.m_animationTime = 0;
	}
	else {
		this.m_animationTime += deltaTime * this.m_timeFactor;
		if(this.m_animationTime >= this.m_animationDuration || this.m_animationTime < 0) {
			this.m_animationTime = this.m_animationTime % this.m_animationDuration;
		}
		if (this.m_animationTime < 0) {
			this.m_animationTime += this.m_animationDuration;
		}
	}

	// update all active animation actions of this model
	var animationActionId = 0;
	while(animationActionId < this.m_listAnimationAction.length) {
		var animationAction = this.m_listAnimationAction[animationActionId];
		// update and check if animation action is still active
		if(animationAction.update(deltaTime)) {
			animationAction.checkCallbacks(animationAction.getTime(), this.m_model);
			animationActionId++;
		}
		else {
			// animation action has ended, destroy and remove it from the animation list
			animationAction.completeCallbacks(this.m_model);
			this.m_listAnimationAction.splice(animationActionId, 1);
		}
	}

	// TODO: update all active animation poses of this model
	// ...

	// update the weight of all active animation cycles of this model
	var accumulatedWeight = 0;
	var accumulatedDuration = 0;
	var animationCycleId = 0;
	while(animationCycleId < this.m_listAnimationCycle.length) {
		var animationCycle = this.m_listAnimationCycle[animationCycleId];
		// update and check if animation cycle is still active
		if(animationCycle.update(deltaTime)) {
			// check if it is in sync. if yes, update accumulated weight and duration
			if(animationCycle.getState() == Cal3D.CalAnimation.State.STATE_SYNC) {
				accumulatedWeight += animationCycle.getWeight();
				accumulatedDuration += animationCycle.getWeight() * animationCycle.getCoreAnimation().getDuration();
			}

			animationCycle.checkCallbacks(this.m_animationTime, this.m_model);
			animationCycleId++;
		}
		else {
			// animation cycle has ended, destroy and remove it from the animation list
			animationCycle.completeCallbacks(this.m_model);
			this.m_listAnimationCycle.splice(animationCycleId, 1);
		}
	}

	// adjust the global animation cycle duration
	if(accumulatedWeight > 0) {
		this.m_animationDuration = accumulatedDuration / accumulatedWeight;
	}
	else {
		this.m_animationDuration = 0;
	}
};

/**
	Update the skeleton of the corresponding model to match the current animation state.
*/
Cal3D.CalMixer.prototype.updateSkeleton = function() {
	// get the skeleton we need to update
	var skeleton = this.m_model.getSkeleton();
	if(!skeleton) 
		return;

	// clear the skeleton state
	skeleton.clearState();

	// get the bone vector of the skeleton
	var vectorBone = skeleton.getVectorBone();

	var translation = new Cal3D.CalVector;
	var rotation = new Cal3D.CalQuaternion;

	// loop through all animation actions
	for(var animationActionId=0; animationActionId<this.m_listAnimationAction.length; animationActionId++) {
		var animationAction = this.m_listAnimationAction[animationActionId];
		
		// get the core animation instance
		var coreAnimation = animationAction.getCoreAnimation();

		// get the list of core tracks of above core animation
		var listCoreTrack = coreAnimation.getListCoreTrack();

		// loop through all core tracks of the core animation
		for(var coreTrackId=0; coreTrackId<listCoreTrack.length; coreTrackId++) {
			// get the appropriate bone of the track
			var bone = vectorBone[ listCoreTrack[coreTrackId].getCoreBoneId() ];

			// get the current translation and rotation
			listCoreTrack[coreTrackId].getState(animationAction.getTime(), translation, rotation);

			// blend the bone state with the new state
			bone.blendState(animationAction.getWeight(), translation, rotation);
		}
	}

	// lock the skeleton state
	skeleton.lockState();

	// loop through all animation cycles
	for(var animationCycleId=0; animationCycleId<this.m_listAnimationCycle.length; animationCycleId++) {
		var animationCycle = this.m_listAnimationCycle[animationCycleId];

		// get the core animation instance
		var coreAnimation = animationCycle.getCoreAnimation();

		// calculate adjusted time
		var animationTime;
		if(animationCycle.getState() == Cal3D.CalAnimation.State.STATE_SYNC) {
			if(this.m_animationDuration == 0)
				animationTime = 0;
			else
				animationTime = this.m_animationTime * coreAnimation.getDuration() / this.m_animationDuration;
		}
		else {
			animationTime = animationCycle.getTime();
		}

		// get the list of core tracks of above core animation
		var listCoreTrack = coreAnimation.getListCoreTrack();

		// loop through all core tracks of the core animation
		for(var coreTrackId=0; coreTrackId<listCoreTrack.length; coreTrackId++) {
			// get the appropriate bone of the track
			var bone = vectorBone[ listCoreTrack[coreTrackId].getCoreBoneId() ];

			// get the current translation and rotation
			listCoreTrack[coreTrackId].getState(animationTime, translation, rotation);

			// blend the bone state with the new state
			bone.blendState(animationCycle.getWeight(), translation, rotation);
		}
	}

	// lock the skeleton state
	skeleton.lockState();

	// let the skeleton calculate its final state
	skeleton.calculateState();
};

/**
	Get the animation time.<br />
	This function returns the animation time of the mixer instance.
	@returns {number} The animation time in seconds.
*/
Cal3D.CalMixer.prototype.getAnimationTime = function() {
	return this.m_animationTime;
};

/**
	Get the animation duration.<br />
	This function returns the animation duration of the mixer instance.
	@returns {number} The animation duration in seconds.
*/
Cal3D.CalMixer.prototype.getAnimationDuration = function() {
	return this.m_animationDuration;
};

/**
	Set the animation time.<br />
	This function sets the animation time of the mixer instance.
	@param {number} animationTime The animation time to be set.
*/
Cal3D.CalMixer.prototype.setAnimationTime = function(animationTime) {
	this.m_animationTime = animationTime;
};

/**
	Set the time factor.<br />
	This function sets the time factor of the mixer instance. This time factor affect only sync animation
	@param {number} timeFactor The time factor to be set.
*/
Cal3D.CalMixer.prototype.setTimeFactor = function(timeFactor) {
	this.m_timeFactor = timeFactor;
};

/**
	Get the time factor.<br />
	This function return the time factor of the mixer instance.
	@returns {number} The time factor.
*/
Cal3D.CalMixer.prototype.getTimeFactor = function() {
	return this.m_timeFactor;
};

/**
	Get the model.<br />
	This function return the CalModel of the mixer instance.
	@returns {Cal3D.CalModel} The model.
*/
Cal3D.CalMixer.prototype.getCalModel = function() {
	return this.m_model;
};

/**
	Get the animation list.<br />
	This function return the animation list of the mixer instance.
	@returns {Array} The animation list.
*/
Cal3D.CalMixer.prototype.getAnimationVector = function() {
	return this.m_vectorAnimation;
};

/**
	Get the list of the action animations.<br />
	This function return the list of the action animations of the mixer instance.
	@returns {Array} The list of the action animations.
*/
Cal3D.CalMixer.prototype.getAnimationActionList = function() {
	return this.m_listAnimationAction;
};

/**
	Get the list of the cycle animations.<br />
	This function return the list of the cycle animations of the mixer instance.
	@returns {Array} The list of the cycle animations.
*/
Cal3D.CalMixer.prototype.getAnimationCycle = function() {
	return this.m_listAnimationCycle;
};

/**
	Examine the given animation and if the first and last keyframe of a given track do not match up, the first key frame is duplicated 
	and added to the end of the track to ensure smooth looping.
	@private
*/
Cal3D.addExtraKeyframeForLoopedAnim = function(coreAnimation) {
	var listCoreTrack = coreAnimation.getListCoreTrack();
	if(listCoreTrack.length == 0)
		return;

	var coreTrackId = 0;
	if(!listCoreTrack[coreTrackId])
		return;

	var lastKeyframe = listCoreTrack[coreTrackId].getCoreKeyframe( listCoreTrack[coreTrackId].getCoreKeyframeCount() - 1 );
	if(!lastKeyframe)
		return;

	if(lastKeyframe.getTime() < coreAnimation.getDuration()) {
		for(coreTrackId=0; coreTrackId<listCoreTrack.length; coreTrackId++) {
			var coreTrack = listCoreTrack[coreTrackId];

			var firstKeyframe = coreTrack.getCoreKeyframe(0);
			var newKeyframe = new Cal3D.CalCoreKeyframe;

			newKeyframe.setTranslation(firstKeyframe.getTranslation());
			newKeyframe.setRotation(firstKeyframe.getRotation());
			newKeyframe.setTime(coreAnimation.getDuration());

			coreTrack.addCoreKeyframe(newKeyframe);
		}
	}
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalMorphTargetMixer
*/
Cal3D.CalMorphTargetMixer = function(model) {
	this.m_model = model;
	this.m_vectorCurrentWeight = [];
	this.m_vectorEndWeight = [];
	this.m_vectorDuration = [];

	var morphAnimationCount = model.getCoreModel().getCoreMorphAnimationCount();
	for(var i=0; i<morphAnimationCount; i++) {
		this.m_vectorCurrentWeight.push(0);
		this.m_vectorEndWeight.push(0);
		this.m_vectorDuration.push(0);
	}
};

/**
	Interpolate the weight of a morph target.<br />
	This function interpolates the weight of a morph target a new value in a given amount of time.
	@param {number} id The ID of the morph target to be blended.
	@param {number} weight  The weight to interpolate the morph target to.
	@param {number} delay The time in seconds until the new weight should be reached.
	@returns {boolean} true if succeeded; false if the morph target does not exist.
*/

Cal3D.CalMorphTargetMixer.prototype.blend = function(id, weight, delay) {
	if(id < 0 || id >= this.m_vectorCurrentWeight.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'morphtargetmixer.js');
		return false;
	}

	this.m_vectorEndWeight[id] = weight;
	this.m_vectorDuration[id] = delay;
	return true;
};

/**
	Fade a morph target out.<br />
	This function fades a morph target out in a given amount of time.
	@param {number} id The ID of the morph target to be faded out.
	@param {number} delay The time in seconds until the the morph target is completely removed.
	@returns {boolean} true if succeeded; false if the morph target does not exist.
*/

Cal3D.CalMorphTargetMixer.prototype.clear = function(id, delay) {
	if(id < 0 || id >= this.m_vectorCurrentWeight.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'morphtargetmixer.js');
		return false;
	}

	this.m_vectorEndWeight[id] = 0;
	this.m_vectorDuration[id] = delay;
	return true;
};

/**
	Get the weight of a morph target.<br />
	@param {number} id The ID of the morph target.
	@returns {number} The weight of the morph target.
*/

Cal3D.CalMorphTargetMixer.prototype.getCurrentWeight = function(id) {
	if(id < 0 || id >= this.m_vectorCurrentWeight.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'morphtargetmixer.js');
		return 0;
	}

	return this.m_vectorCurrentWeight[id];
};

/**
	Get the weight of the base vertices.<br />
	@returns {number} The weight of the base vertices.
*/
Cal3D.CalMorphTargetMixer.prototype.getCurrentWeightBase = function() {
	var currentWeight = 1;
	for(var i=0; i<this.m_vectorCurrentWeight.length; i++) {
		currentWeight -= this.m_vectorCurrentWeight[i];
	}

	return currentWeight;
};

/**
	Get the number of morph targets this morph target mixer mixes.<br />
	@returns {number} The number of morph targets this morph target mixer mixes.
*/
Cal3D.CalMorphTargetMixer.prototype.getMorphTargetCount = function() {
	return this.m_vectorCurrentWeight.length;
};

/**
	Update all morph targets.<br />
	This function updates all morph targets of the mixer instance for a given amount of time.
	@param {number} deltaTime The elapsed time in seconds since the last update.
*/
Cal3D.CalMorphTargetMixer.prototype.update = function(deltaTime) {
	for(var i=0; i<this.m_vectorCurrentWeight.length; i++) {
		var currentWeight = this.m_vectorCurrentWeight[i];
		var endWeight = this.m_vectorEndWeight[i];
		var duration = this.m_vectorDuration[i];

		if(deltaTime >= duration) {
			this.m_vectorCurrentWeight[i] = endWeight;
			this.m_vectorDuration[i] = 0;
		}
		else {
			this.m_vectorCurrentWeight[i] += (endWeight - currentWeight) * deltaTime / duration;
			this.m_vectorDuration[i] -= deltaTime;
		}
	}

	for(var morphAnimationID=0; morphAnimationID<this.m_vectorCurrentWeight.length; morphAnimationID++) {
		var coreMorphAnimation = this.m_model.getCoreModel().getCoreMorphAnimation(morphAnimationID);

		var vectorCoreMeshID = coreMorphAnimation.getVectorCoreMeshID();
		var vectorMorphTargetID = coreMorphAnimation.getVectorMorphTargetID();

		for(var i=0; i<vectorCoreMeshID.length; i++) {
			var vectorSubmesh = this.m_model.getMesh(vectorCoreMeshID[i]).getVectorSubmesh();

			var submeshCount = vectorSubmesh.length;
			for(var submeshId=0; submeshId<submeshCount; submeshId++) {
				vectorSubmesh[submeshId].setMorphTargetWeight(vectorMorphTargetID[i],  this.m_vectorCurrentWeight[morphAnimationID]);
			}
		}
	}
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalPhysique
*/
Cal3D.CalPhysique = function(model) {
	this.m_model = model;
	this.m_normalize = true;
	this.m_axisFactorX = 1;
	this.m_axisFactorY = 1;
	this.m_axisFactorZ = 1;
};

/**
	Calculate the transformed tangent space data.<br />
	This function calculates and returns the transformed tangent space data of a specific submesh.
	@param {Cal3D.CalSubmesh} submesh The submesh from which the tangent space data should be calculated and returned.
	@param {number} mapId The ID of the texture map.
	@param {Array} tangentSpaceBuffer The user-provided buffer where the tangent space data is written to. This is an output parameter.
	@param {number} stride (Optional). The offset from the start of one data to the start of next data, default to 4.
	@returns {number} The number of tangent spaces written to the buffer.
*/
Cal3D.CalPhysique.prototype.calculateTangentSpaces = function(submesh, mapId, tangentSpaceBuffer, stride) {
	if(mapId < 0 || mapId >= submesh.getCoreSubmesh().getVectorVectorTangentSpace().length)
		return 0;

	if(stride == undefined || stride <= 0)
		stride = 4;

	if(tangentSpaceBuffer.length < submesh.getVertexCount() * stride)
		return 0;

	// get bone vector of the skeleton
	var vectorBone = this.m_model.getSkeleton().getVectorBone();

	// get vertex vector of the submesh
	var vectorVertex = submesh.getCoreSubmesh().getVectorVertex();

	// get tangent space vector of the submesh
	var vectorTangentSpace = submesh.getCoreSubmesh().getVectorVectorTangentSpace()[mapId];

	// get the number of vertices
	var vertexCount = submesh.getVertexCount();

	// calculate tangent for all submesh vertices
	var ti = 0;
	var v = new Cal3D.CalVector;
	for(var vertexId=0; vertexId<vertexCount; vertexId++) {
		var tangentSpace = vectorTangentSpace[vertexId];

		// get the vertex
		var vertex = vectorVertex[vertexId];

		// initialize tangent
		var tx, ty, tz;
		tx = 0;
		ty = 0;
		tz = 0;

		// blend together all vertex influences
		var influenceCount = vertex.vectorInfluence.length;
		for(var influenceId=0; influenceId<influenceCount; influenceId++) {
			// get the influence
			var influence = vertex.vectorInfluence[influenceId];

			// get the bone of the influence vertex
			var bone = vectorBone[influence.boneId];

			// transform normal with current state of the bone
			v.assign(tangentSpace.tangent);
			v.multMatrixLocal(bone.getTransformMatrix());

			tx += influence.weight * v.x;
			ty += influence.weight * v.y;
			tz += influence.weight * v.z;
		}

		// re-normalize tangent if necessary
		if(this.m_normalize) {
			tx /= this.m_axisFactorX;
			ty /= this.m_axisFactorY;
			tz /= this.m_axisFactorZ;

			var scale = 1 / Math.sqrt(tx * tx + ty * ty + tz * tz);

			tangentSpaceBuffer[ti    ] = tx * scale;
			tangentSpaceBuffer[ti + 1] = ty * scale;
			tangentSpaceBuffer[ti + 2] = tz * scale;
		}
		else {
			tangentSpaceBuffer[ti    ] = tx;
			tangentSpaceBuffer[ti + 1] = ty;
			tangentSpaceBuffer[ti + 2] = tz;
		}

		tangentSpaceBuffer[ti + 3] = tangentSpace.crossFactor;

		// next vertex position in buffer
		ti += stride;
	}

	return vertexCount;
};

/**
	Calculate the transformed normal data.<br />
	This function calculates and returns the transformed normal data of a specific submesh.
	@param {Cal3D.CalSubmesh} submesh The submesh from which the normal data should be calculated and returned.
	@param {Array} normalBuffer The user-provided buffer where the normal data is written to.  This is an output parameter.
	@param {number} stride (Optional). The offset from the start of one data to the start of next data, default to 3.
	@returns {number} The number of normals written to the buffer.
*/
Cal3D.CalPhysique.prototype.calculateNormals = function(submesh, normalBuffer, stride) {
	if(stride == undefined || stride <= 0)
		stride = 3;

	if(normalBuffer.length < submesh.getVertexCount() * stride)
		return 0;

	// get bone vector of the skeleton
	var vectorBone = this.m_model.getSkeleton().getVectorBone();

	// get vertex vector of the submesh
	var vectorVertex = submesh.getCoreSubmesh().getVectorVertex();

	// get the number of vertices
	var vertexCount = submesh.getVertexCount();

	// get the sub morph target vector from the core sub mesh
	var vectorSubMorphTarget = submesh.getCoreSubmesh().getVectorCoreSubMorphTarget();

	// calculate the base weight
	var baseWeight = submesh.getBaseWeight();

	// get the number of morph targets
	var morphTargetCount = submesh.getMorphTargetWeightCount();

	// calculate normal for all submesh vertices
	var ni = 0;
	var normal = new Cal3D.CalVector;
	var v = new Cal3D.CalVector
	for(var vertexId=0; vertexId<vertexCount; vertexId++) {
		// get the vertex
		var vertex = vectorVertex[vertexId];

		// blend the morph targets
		if(baseWeight == 1) {
			normal.x = vertex.normal.x;
			normal.y = vertex.normal.y;
			normal.z = vertex.normal.z;
		}
		else {
			normal.x = baseWeight * vertex.normal.x;
			normal.y = baseWeight * vertex.normal.y;
			normal.z = baseWeight * vertex.normal.z;

			for(var morphTargetId=0; morphTargetId<morphTargetCount; morphTargetId++) {
				var blendVertex = vectorSubMorphTarget[morphTargetId].getVectorBlendVertex()[vertexId];
				var currentWeight = submesh.getMorphTargetWeight(morphTargetId);
				normal.x += currentWeight * blendVertex.normal.x;
				normal.y += currentWeight * blendVertex.normal.y;
				normal.z += currentWeight * blendVertex.normal.z;
			}
		}

		// initialize normal
		var nx, ny, nz;
		nx = 0;
		ny = 0;
		nz = 0;

		// blend together all vertex influences
		var influenceCount = vertex.vectorInfluence.length;
		if(influenceCount == 0) {
			nx = normal.x;
			ny = normal.y;
			nz = normal.z;
		} 
		else  {
			for(var influenceId=0; influenceId<influenceCount; influenceId++) {
				// get the influence
				var influence = vertex.vectorInfluence[influenceId];

				// get the bone of the influence vertex
				var bone = vectorBone[influence.boneId];

				// transform normal with current state of the bone
				v.assign(normal);
				v.multMatrixLocal(bone.getTransformMatrix());

				nx += influence.weight * v.x;
				ny += influence.weight * v.y;
				nz += influence.weight * v.z;
			}
		}

		// re-normalize normal if necessary
		if (this.m_normalize) {
			nx /= this.m_axisFactorX;
			ny /= this.m_axisFactorY;
			nz /= this.m_axisFactorZ;

			var scale = 1 / Math.sqrt(nx * nx + ny * ny + nz * nz);

			normalBuffer[ni    ] = nx * scale;
			normalBuffer[ni + 1] = ny * scale;
			normalBuffer[ni + 2] = nz * scale;
		}
		else {
			normalBuffer[ni    ] = nx;
			normalBuffer[ni + 1] = ny;
			normalBuffer[ni + 2] = nz;
		}

		ni += stride;
	}

	return vertexCount;
};

/**
	Calculate the transformed vertex data.<br />
	This function calculates and returns the transformed vertex data of a specific submesh.
	@param {Cal3D.CalSubmesh} submesh The submesh from which the vertex data should be calculated and returned.
	@param {Array} vertexBuffer The user-provided buffer where the vertex data is written to.  This is an output parameter.
	@param {number} stride (Optional). The offset from the start of one data to the start of next data, default to 3.
	@returns {number} The number of vertices written to the buffer.
*/
Cal3D.CalPhysique.prototype.calculateVertices = function(submesh, vertexBuffer, stride) {
	if(stride == undefined || stride <= 0)
		stride = 3;

	if(vertexBuffer.length < submesh.getVertexCount() * stride)
		return 0;

	// get bone vector of the skeleton
	var vectorBone = this.m_model.getSkeleton().getVectorBone();

	// get vertex vector of the core submesh
	var vectorVertex = submesh.getCoreSubmesh().getVectorVertex();

	// get physical property vector of the core submesh
	var vectorPhysicalProperty = submesh.getCoreSubmesh().getVectorPhysicalProperty();

	// get the number of vertices
	var vertexCount = submesh.getVertexCount();

	// get the sub morph target vector from the core sub mesh
	var vectorSubMorphTarget = submesh.getCoreSubmesh().getVectorCoreSubMorphTarget();

	// calculate the base weight
	var baseWeight = submesh.getBaseWeight();

	// get the number of morph targets
	var morphTargetCount = submesh.getMorphTargetWeightCount();

	// calculate all submesh vertices
	var vi = 0;
	var position = new Cal3D.CalVector;
	var v = new Cal3D.CalVector;
	for(var vertexId=0; vertexId<vertexCount; vertexId++) {
		// get the vertex
		var vertex = vectorVertex[vertexId];

		// blend the morph targets
		position.assign(0, 0, 0);
		if(baseWeight == 1) {
			position.x = vertex.position.x;
			position.y = vertex.position.y;
			position.z = vertex.position.z;
		}
		else {
			position.x = baseWeight * vertex.position.x;
			position.y = baseWeight * vertex.position.y;
			position.z = baseWeight * vertex.position.z;

			for(var morphTargetId=0; morphTargetId<morphTargetCount; morphTargetId++) {
				var blendVertex = vectorSubMorphTarget[morphTargetId].getVectorBlendVertex()[vertexId];
				var currentWeight = submesh.getMorphTargetWeight(morphTargetId);
				position.x += currentWeight * blendVertex.position.x;
				position.y += currentWeight * blendVertex.position.y;
				position.z += currentWeight * blendVertex.position.z;
			}
		}

		// initialize vertex
		var x, y, z;
		x = 0;
		y = 0;
		z = 0;

		// blend together all vertex influences
		var influenceCount = vertex.vectorInfluence.length;
		if(influenceCount == 0) {
			x = position.x;
			y = position.y;
			z = position.z;
		} 
		else {
			for(var influenceId=0; influenceId<influenceCount; influenceId++) {
				// get the influence
				var influence = vertex.vectorInfluence[influenceId];

				// get the bone of the influence vertex
				var bone = vectorBone[influence.boneId];

				// transform vertex with current state of the bone
				v.assign(position);
				v.multMatrixLocal(bone.getTransformMatrix());
				v.addLocal(bone.getTranslationBoneSpace());

				x += influence.weight * v.x;
				y += influence.weight * v.y;
				z += influence.weight * v.z;
			}
		}

		// save vertex position
		if(submesh.getCoreSubmesh().getSpringCount() > 0 && submesh.hasInternalData()) {
			// get the pgysical property of the vertex
			var physicalProperty = vectorPhysicalProperty[vertexId];

			// assign new vertex position if there is no vertex weight
			if(physicalProperty.weight == 0) {
				vertexBuffer[vi    ] = x * this.m_axisFactorX;
				vertexBuffer[vi + 1] = y * this.m_axisFactorY;
				vertexBuffer[vi + 2] = z * this.m_axisFactorZ;
			}
		}
		else {
			vertexBuffer[vi    ] = x * this.m_axisFactorX;
			vertexBuffer[vi + 1] = y * this.m_axisFactorY;
			vertexBuffer[vi + 2] = z * this.m_axisFactorZ;
		}

		vi += stride;
	}

	return vertexCount;
};

/**
	Calculate a transformed vertex.<br />
	This function calculates and returns a transformed vertex of a specific submesh.
	@param {Cal3D.CalSubmesh} submesh The submesh from which the vertex should be calculated and returned.
	@param {number} vertexId The ID of the vertex that should be transformed.
	@returns {Cal3D.CalVector} The transformed vertex.
*/
Cal3D.CalPhysique.prototype.calculateVertex = function(submesh, vertexId) {
	// get bone vector of the skeleton
	var vectorBone = this.m_model.getSkeleton().getVectorBone();

	// get vertex of the core submesh
	var vectorVertex = submesh.getCoreSubmesh().getVectorVertex();

	// get the sub morph target vector from the core sub mesh
	var vectorSubMorphTarget = submesh.getCoreSubmesh().getVectorCoreSubMorphTarget();

	// calculate the base weight
	var baseWeight = submesh.getBaseWeight();

	// get the number of morph targets
	var morphTargetCount = submesh.getMorphTargetWeightCount();

	// get the vertex
	var vertex = vectorVertex[vertexId];

	// blend the morph targets
	var position = new Cal3D.CalVector(0, 0, 0);
	if(baseWeight == 1) {
		position.x = vertex.position.x;
		position.y = vertex.position.y;
		position.z = vertex.position.z;
	}
	else {
		position.x = baseWeight * vertex.position.x;
		position.y = baseWeight * vertex.position.y;
		position.z = baseWeight * vertex.position.z;

		for(var morphTargetId=0; morphTargetId<morphTargetCount; morphTargetId++) {
			var blendVertex = vectorSubMorphTarget[morphTargetId].getVectorBlendVertex()[vertexId];
			var currentWeight = submesh.getMorphTargetWeight(morphTargetId);
			position.x += currentWeight * blendVertex.position.x;
			position.y += currentWeight * blendVertex.position.y;
			position.z += currentWeight * blendVertex.position.z;
		}
	}

	// initialize vertex
	var x, y, z;
	x = 0;
	y = 0;
	z = 0;

	// blend together all vertex influences
	var v = new Cal3D.CalVector;
	var influenceCount = vertex.vectorInfluence.length;
	if(influenceCount == 0) {
		x = position.x;
		y = position.y;
		z = position.z;
	} 
	else {
		for(var influenceId=0; influenceId<influenceCount; influenceId++) {
			// get the influence
			var influence = vertex.vectorInfluence[influenceId];

			// get the bone of the influence vertex
			var bone = vectorBone[influence.boneId];

			// transform vertex with current state of the bone
			v.assign(position);
			v.multMatrixLocal(bone.getTransformMatrix());
			v.addLocal(bone.getTranslationBoneSpace());
			
			x += influence.weight * v.x;
			y += influence.weight * v.y;
			z += influence.weight * v.z;
		}
	}

	return new Cal3D.CalVector(x * this.m_axisFactorX, y * this.m_axisFactorY, z * this.m_axisFactorZ);
};

/**
	Calculate the transformed vertex and vertex normal data.<br />
	This function calculates and returns the transformed vertex and the transformed normal datadata of a specific submesh.
	@param {Cal3D.CalSubmesh} submesh The submesh from which the vertex and normal data should be calculated and returned.
	@param {Array} vertexBuffer The user-provided buffer where the vertex and normal data is written to.  This is an output parameter.
	@param {number} stride (Optional). The offset from the start of one data to the start of next data, default to 6.
	@returns {number} The number of vertices written to the buffer.
*/
Cal3D.CalPhysique.prototype.calculateVerticesAndNormals = function(submesh, vertexBuffer, stride) {
	if(stride == undefined || stride <= 0)
		stride = 6;

	if(vertexBuffer.length < submesh.getVertexCount() * stride)
		return 0;

	// get bone vector of the skeleton
	var vectorBone = this.m_model.getSkeleton().getVectorBone();

	// get vertex vector of the core submesh
	var vectorVertex = submesh.getCoreSubmesh().getVectorVertex();

	// get physical property vector of the core submesh
	var vectorPhysicalProperty = submesh.getCoreSubmesh().getVectorPhysicalProperty();

	// get the number of vertices
	var vertexCount = submesh.getVertexCount();

	// get the sub morph target vector from the core sub mesh
	var vectorSubMorphTarget = submesh.getCoreSubmesh().getVectorCoreSubMorphTarget();

	// calculate the base weight
	var baseWeight = submesh.getBaseWeight();

	// get the number of morph targets
	var morphTargetCount = submesh.getMorphTargetWeightCount();

	// calculate all submesh vertices
	var vi = 0;
	var v = new Cal3D.CalVector;
	var n = new Cal3D.CalVector;
	var position = new Cal3D.CalVector;
	var normal = new Cal3D.CalVector;
	for(var vertexId=0; vertexId<vertexCount; vertexId++) {
		// get the vertex
		var vertex = vectorVertex[vertexId];

		// blend the morph targets
		position.assign(0, 0, 0);
		normal.assign(0, 0, 0);
		if(baseWeight == 1) {
			position.x = vertex.position.x;
			position.y = vertex.position.y;
			position.z = vertex.position.z;
			normal.x = vertex.normal.x;
			normal.y = vertex.normal.y;
			normal.z = vertex.normal.z;
		}
		else {
			position.x = baseWeight * vertex.position.x;
			position.y = baseWeight * vertex.position.y;
			position.z = baseWeight * vertex.position.z;
			normal.x = baseWeight * vertex.normal.x;
			normal.y = baseWeight * vertex.normal.y;
			normal.z = baseWeight * vertex.normal.z;

			for(var morphTargetId=0; morphTargetId<morphTargetCount; morphTargetId++) {
				var blendVertex = vectorSubMorphTarget[morphTargetId].getVectorBlendVertex()[vertexId];
				var currentWeight = submesh.getMorphTargetWeight(morphTargetId);
				position.x += currentWeight * blendVertex.position.x;
				position.y += currentWeight * blendVertex.position.y;
				position.z += currentWeight * blendVertex.position.z;
				normal.x += currentWeight * blendVertex.normal.x;
				normal.y += currentWeight * blendVertex.normal.y;
				normal.z += currentWeight * blendVertex.normal.z;
			}
		}

		// initialize vertex
		var x, y, z;
		x = 0;
		y = 0;
		z = 0;

		// initialize normal
		var nx, ny, nz;
		nx = 0;
		ny = 0;
		nz = 0;

		// blend together all vertex influences
		var influenceCount = vertex.vectorInfluence.length;
		if(influenceCount == 0) {
			x = position.x;
			y = position.y;
			z = position.z;
			nx = normal.x;
			ny = normal.y;
			nz = normal.z;
		} 
		else {
			for(var influenceId=0; influenceId<influenceCount; influenceId++) {
				// get the influence
				var influence = vertex.vectorInfluence[influenceId];

				// get the bone of the influence vertex
				var bone = vectorBone[influence.boneId];

				// transform vertex with current state of the bone
				v.assign(position);
				v.multMatrixLocal(bone.getTransformMatrix());
				v.addLocal(bone.getTranslationBoneSpace());
				
				x += influence.weight * v.x;
				y += influence.weight * v.y;
				z += influence.weight * v.z;

				// transform normal with current state of the bone
				n.assign(normal);
				n.multMatrixLocal(bone.getTransformMatrix());

				nx += influence.weight * n.x;
				ny += influence.weight * n.y;
				nz += influence.weight * n.z;
			}
		}

		// save vertex position
		if(submesh.getCoreSubmesh().getSpringCount() > 0 && submesh.hasInternalData()) {
			// get the pgysical property of the vertex
			var physicalProperty = vectorPhysicalProperty[vertexId];

			// assign new vertex position if there is no vertex weight
			if(physicalProperty.weight == 0) {
				vertexBuffer[vi    ] = x * this.m_axisFactorX;
				vertexBuffer[vi + 1] = y * this.m_axisFactorY;
				vertexBuffer[vi + 2] = z * this.m_axisFactorZ;
			}
		}
		else {
			vertexBuffer[vi    ] = x * this.m_axisFactorX;
			vertexBuffer[vi + 1] = y * this.m_axisFactorY;
			vertexBuffer[vi + 2] = z * this.m_axisFactorZ;
		}

		// re-normalize normal if necessary
		if (this.m_normalize) {
			nx /= this.m_axisFactorX;
			ny /= this.m_axisFactorY;
			nz /= this.m_axisFactorZ;

			var scale = 1 / Math.sqrt(nx * nx + ny * ny + nz * nz);

			vertexBuffer[vi + 3] = nx * scale;
			vertexBuffer[vi + 4] = ny * scale;
			vertexBuffer[vi + 5] = nz * scale;
		}
		else {
			vertexBuffer[vi + 3] = nx;
			vertexBuffer[vi + 4] = ny;
			vertexBuffer[vi + 5] = nz;
		}

		vi += stride;
	}

	return vertexCount;
};

/**
	Calculate the transformed vertex, vertex normal and texture coordinate data.<br />
	This function calculates and returns the transformed vertex, the transformed normal data and the texture coordinates of a specific submesh.
	@param {Cal3D.CalSubmesh} submesh The submesh from which the vertex, normal and texture coordinate data should be calculated and returned.
	@param {Array} vertexBuffer The user-provided buffer where the vertex, normal and texture coordinate data is written to.  This is an output parameter.
	@param {number} numTexCoords The number of texture maps whose texture coordinates is required, since there can be 0 or more than 1 texture maps defined.
	@returns {number} The number of vertices written to the buffer.
*/
Cal3D.CalPhysique.prototype.calculateVerticesNormalsAndTexCoords = function(submesh, vertexBuffer, numTexCoords) {
	if(numTexCoords == undefined)
		numTexCoords = 1;

	// get bone vector of the skeleton
	var vectorBone = this.m_model.getSkeleton().getVectorBone();

	// get vertex vector of the core submesh
	var vectorVertex = submesh.getCoreSubmesh().getVectorVertex();

	// get the texture coordinate vector vector
	var vectorvectorTextureCoordinate = submesh.getCoreSubmesh().getVectorVectorTextureCoordinate();

	var textureCoordinateCount = vectorvectorTextureCoordinate.length;

	// check if the given texture coord count is valid
	if(numTexCoords < 0 || numTexCoords > textureCoordinateCount) {
		if(textureCoordinateCount != 0)
			return -1;
	}

	// get physical property vector of the core submesh
	var vectorPhysicalProperty = submesh.getCoreSubmesh().getVectorPhysicalProperty();

	// get the number of vertices
	var vertexCount = submesh.getVertexCount();

	// get the sub morph target vector from the core sub mesh
	var vectorSubMorphTarget = submesh.getCoreSubmesh().getVectorCoreSubMorphTarget();

	// calculate the base weight
	var baseWeight = submesh.getBaseWeight();

	// get the number of morph targets
	var morphTargetCount = submesh.getMorphTargetWeightCount();

	// calculate all submesh vertices
	var vi = 0;
	var position = new Cal3D.CalVector;
	var normal = new Cal3D.CalVector;
	for(var vertexId=0; vertexId<vertexCount; vertexId++) {
		// get the vertex
		var vertex = vectorVertex[vertexId];

		// blend the morph targets
		position.assign(0, 0, 0);
		normal.assign(0, 0, 0);
		if(baseWeight == 1) {
			position.x = vertex.position.x;
			position.y = vertex.position.y;
			position.z = vertex.position.z;
			normal.x = vertex.normal.x;
			normal.y = vertex.normal.y;
			normal.z = vertex.normal.z;
		}
		else {
			position.x = baseWeight * vertex.position.x;
			position.y = baseWeight * vertex.position.y;
			position.z = baseWeight * vertex.position.z;
			normal.x = baseWeight * vertex.normal.x;
			normal.y = baseWeight * vertex.normal.y;
			normal.z = baseWeight * vertex.normal.z;

			for(var morphTargetId=0; morphTargetId<morphTargetCount; morphTargetId++) {
				var blendVertex = vectorSubMorphTarget[morphTargetId].getVectorBlendVertex()[vertexId];
				var currentWeight = submesh.getMorphTargetWeight(morphTargetId);
				position.x += currentWeight * blendVertex.position.x;
				position.y += currentWeight * blendVertex.position.y;
				position.z += currentWeight * blendVertex.position.z;
				normal.x += currentWeight * blendVertex.normal.x;
				normal.y += currentWeight * blendVertex.normal.y;
				normal.z += currentWeight * blendVertex.normal.z;
			}
		}

		// initialize vertex
		var x, y, z;
		x = 0;
		y = 0;
		z = 0;

		// initialize normal
		var nx, ny, nz;
		nx = 0;
		ny = 0;
		nz = 0;

		// blend together all vertex influences
		var influenceCount = vertex.vectorInfluence.length;
		if(influenceCount == 0) {
			x = position.x;
			y = position.y;
			z = position.z;
			nx = normal.x;
			ny = normal.y;
			nz = normal.z;
		} 
		else {
			for(var influenceId=0; influenceId<influenceCount; influenceId++) {
				// get the influence
				var influence = vertex.vectorInfluence[influenceId];

				// get the bone of the influence vertex
				var bone = vectorBone[influence.boneId];

				// transform vertex with current state of the bone
				var v = new Cal3D.CalVector(position);
				v.multMatrixLocal(bone.getTransformMatrix());
				v.addLocal(bone.getTranslationBoneSpace());

				x += influence.weight * v.x;
				y += influence.weight * v.y;
				z += influence.weight * v.z;

				// transform normal with current state of the bone
				var n = new Cal3D.CalVector(normal);
				n.multMatrixLocal(bone.getTransformMatrix());

				nx += influence.weight * n.x;
				ny += influence.weight * n.y;
				nz += influence.weight * n.z;
			}
		}

		// save vertex position
		if(submesh.getCoreSubmesh().getSpringCount() > 0 && submesh.hasInternalData()) {
			// get the pgysical property of the vertex
			var physicalProperty = vectorPhysicalProperty[vertexId];

			// assign new vertex position if there is no vertex weight
			if(physicalProperty.weight == 0) {
				vertexBuffer[vi    ] = x * this.m_axisFactorX;
				vertexBuffer[vi + 1] = y * this.m_axisFactorY;
				vertexBuffer[vi + 2] = z * this.m_axisFactorZ;
			}
		}
		else {
			vertexBuffer[vi    ] = x * this.m_axisFactorX;
			vertexBuffer[vi + 1] = y * this.m_axisFactorY;
			vertexBuffer[vi + 2] = z * this.m_axisFactorZ;
		}

		// re-normalize normal if necessary
		if (this.m_normalize) {
			nx /= this.m_axisFactorX;
			ny /= this.m_axisFactorY;
			nz /= this.m_axisFactorZ;

			var scale = 1 / Math.sqrt(nx * nx + ny * ny + nz * nz);

			vertexBuffer[vi + 3] = nx * scale;
			vertexBuffer[vi + 4] = ny * scale;
			vertexBuffer[vi + 5] = nz * scale;
		}
		else {
			vertexBuffer[vi + 3] = nx;
			vertexBuffer[vi + 4] = ny;
			vertexBuffer[vi + 5] = nz;
		}

		vi += 6;

		if(textureCoordinateCount == 0) {
			vi += numTexCoords * 2;
		}
		else {
			for(var mapId=0; mapId<numTexCoords; mapId++) {
				var textureCoord = vectorvectorTextureCoordinate[mapId][vertexId];
				vertexBuffer[vi    ] = textureCoord.u;
				vertexBuffer[vi + 1] = textureCoord.v;

				vi += 2;
			}
		}
	}

	return vertexCount;
};

/**
	Update all the internally handled attached meshes.
	This function updates all the attached meshes of the model that are handled internally.
*/
Cal3D.CalPhysique.prototype.update = function() {
	// get the attached meshes vector
	var vectorMesh = this.m_model.getVectorMesh();

	// loop through all the attached meshes
	for(var meshId=0; meshId<vectorMesh.length; meshId++) {
		// get the submesh vector of the mesh
		var vectorSubmesh = vectorMesh[meshId].getVectorSubmesh();

		// loop through all the submeshes of the mesh
		for(var submeshId=0; submeshId<vectorSubmesh.length; submeshId++) {
			var submesh = vectorSubmesh[submeshId];
			// check if the submesh handles vertex data internally
			if(submesh.hasInternalData()) {
				// calculate the transformed vertices and normals and store them in the submesh
				var vectorVertex = submesh.getVectorVertex();
				var vectorNormal = submesh.getVectorNormal();
				this.calculateVerticesAndNormalsInternal(submesh, vectorVertex, vectorNormal);

				var vectorTangentSpaceCount = submesh.getVectorVectorTangentSpace().length;
				for(var mapId=0; mapId<vectorTangentSpaceCount; mapId++) {
					if(submesh.isTangentsEnabled(mapId)) {
						var vectorTangentSpace = submesh.getVectorVectorTangentSpace()[mapId];
						this.calculateTangentSpacesInternal(submesh, mapId, vectorTangentSpace);
					}
				}
			}
		}
	}
};

/**
	Set the normalization flag.<br />
	This function sets the normalization flag on or off. If off, the normals calculated by Cal3D will not be normalized. Instead, 
	this transform is left up to the user.
	@param {boolean} normalize The normalization flag.
*/
Cal3D.CalPhysique.prototype.setNormalization = function(normalize) {
	this.m_normalize = normalize;
};

/**
	Set the scale factor along the X-axis.<br />
	@param {number} factor The scale factor.
*/
Cal3D.CalPhysique.prototype.setAxisFactorX = function(factor) {
	this.m_axisFactorX = factor;
	this.m_normalize = true;	
};

/**
	Set the scale factor along the Y-axis.<br />
	@param {number} factor The scale factor.
*/
Cal3D.CalPhysique.prototype.setAxisFactorY = function(factor) {
	this.m_axisFactorY = factor;
	this.m_normalize = true;	
};

/**
	Set the scale factor along the Z-axis.<br />
	@param {number} factor The scale factor.
*/
Cal3D.CalPhysique.prototype.setAxisFactorZ = function(factor) {
	this.m_axisFactorZ = factor;
	this.m_normalize = true;	
};

/**
	@private
*/
Cal3D.CalPhysique.prototype.calculateVerticesAndNormalsInternal = function(submesh, veritces, normals) {
	if(veritces.length < submesh.getVertexCount() || normals.length < submesh.getVertexCount())
		throw 'internal error: buffer is not large enough to contain all the vertex/normal data';

	// get bone vector of the skeleton
	var vectorBone = this.m_model.getSkeleton().getVectorBone();

	// get vertex vector of the core submesh
	var vectorVertex = submesh.getCoreSubmesh().getVectorVertex();

	// get physical property vector of the core submesh
	var vectorPhysicalProperty = submesh.getCoreSubmesh().getVectorPhysicalProperty();

	// get the number of vertices
	var vertexCount = submesh.getVertexCount();

	// get the sub morph target vector from the core sub mesh
	var vectorSubMorphTarget = submesh.getCoreSubmesh().getVectorCoreSubMorphTarget();

	// calculate the base weight
	var baseWeight = submesh.getBaseWeight();

	// get the number of morph targets
	var morphTargetCount = submesh.getMorphTargetWeightCount();

	// calculate all submesh vertices
	var v = new Cal3D.CalVector;
	var n = new Cal3D.CalVector;
	var position = new Cal3D.CalVector;
	var normal = new Cal3D.CalVector;
	for(var vertexId=0; vertexId<vertexCount; vertexId++) {
		// get the vertex
		var vertex = vectorVertex[vertexId];

		// blend the morph targets
		position.assign(0, 0, 0);
		normal.assign(0, 0, 0);
		if(baseWeight == 1) {
			position.x = vertex.position.x;
			position.y = vertex.position.y;
			position.z = vertex.position.z;
			normal.x = vertex.normal.x;
			normal.y = vertex.normal.y;
			normal.z = vertex.normal.z;
		}
		else {
			position.x = baseWeight * vertex.position.x;
			position.y = baseWeight * vertex.position.y;
			position.z = baseWeight * vertex.position.z;
			normal.x = baseWeight * vertex.normal.x;
			normal.y = baseWeight * vertex.normal.y;
			normal.z = baseWeight * vertex.normal.z;

			for(var morphTargetId=0; morphTargetId<morphTargetCount; morphTargetId++) {
				var blendVertex = vectorSubMorphTarget[morphTargetId].getVectorBlendVertex()[vertexId];
				var currentWeight = submesh.getMorphTargetWeight(morphTargetId);
				position.x += currentWeight * blendVertex.position.x;
				position.y += currentWeight * blendVertex.position.y;
				position.z += currentWeight * blendVertex.position.z;
				normal.x += currentWeight * blendVertex.normal.x;
				normal.y += currentWeight * blendVertex.normal.y;
				normal.z += currentWeight * blendVertex.normal.z;
			}
		}

		// initialize vertex
		var x, y, z;
		x = 0;
		y = 0;
		z = 0;

		// initialize normal
		var nx, ny, nz;
		nx = 0;
		ny = 0;
		nz = 0;

		// blend together all vertex influences
		var influenceCount = vertex.vectorInfluence.length;
		if(influenceCount == 0) {
			x = position.x;
			y = position.y;
			z = position.z;
			nx = normal.x;
			ny = normal.y;
			nz = normal.z;
		} 
		else {
			for(var influenceId=0; influenceId<influenceCount; influenceId++) {
				// get the influence
				var influence = vertex.vectorInfluence[influenceId];

				// get the bone of the influence vertex
				var bone = vectorBone[influence.boneId];

				// transform vertex with current state of the bone
				v.assign(position);
				v.multMatrixLocal(bone.getTransformMatrix());
				v.addLocal(bone.getTranslationBoneSpace());
				
				x += influence.weight * v.x;
				y += influence.weight * v.y;
				z += influence.weight * v.z;

				// transform normal with current state of the bone
				n.assign(normal);
				n.multMatrixLocal(bone.getTransformMatrix());

				nx += influence.weight * n.x;
				ny += influence.weight * n.y;
				nz += influence.weight * n.z;
			}
		}

		if(!veritces[vertexId])
			veritces[vertexId] = new Cal3D.CalVector;
		if(!normals[vertexId])
			normals[vertexId] = new Cal3D.CalVector;

		// save vertex position
		if(submesh.getCoreSubmesh().getSpringCount() > 0 && submesh.hasInternalData()) {
			// get the pgysical property of the vertex
			var physicalProperty = vectorPhysicalProperty[vertexId];

			// assign new vertex position if there is no vertex weight
			if(physicalProperty.weight == 0) {
				veritces[vertexId].assign(x * this.m_axisFactorX, y * this.m_axisFactorY, z * this.m_axisFactorZ);
			}
		}
		else {
			veritces[vertexId].assign(x * this.m_axisFactorX, y * this.m_axisFactorY, z * this.m_axisFactorZ);
		}

		// re-normalize normal if necessary
		if (this.m_normalize) {
			nx /= this.m_axisFactorX;
			ny /= this.m_axisFactorY;
			nz /= this.m_axisFactorZ;

			var scale = 1 / Math.sqrt(nx * nx + ny * ny + nz * nz);

			normals[vertexId].assign(nx * scale, ny * scale, nz * scale);
		}
		else {
			normals[vertexId].assign(nx, ny, nz);
		}
	}

	return vertexCount;
};

/**
	@private
*/
Cal3D.CalPhysique.prototype.calculateTangentSpacesInternal = function(submesh, mapId, tangentSpaces) {
	if(mapId < 0 || mapId >= submesh.getCoreSubmesh().getVectorVectorTangentSpace().length)
		throw 'internal error: mapId is out of range';

	if(tangentSpaces.length < submesh.getVertexCount())
		throw 'internal error: buffer is not large enough to contain all the tangent space data';

	// get bone vector of the skeleton
	var vectorBone = this.m_model.getSkeleton().getVectorBone();

	// get vertex vector of the submesh
	var vectorVertex = submesh.getCoreSubmesh().getVectorVertex();

	// get tangent space vector of the submesh
	var vectorTangentSpace = submesh.getCoreSubmesh().getVectorVectorTangentSpace()[mapId];

	// get the number of vertices
	var vertexCount = submesh.getVertexCount();

	// calculate tangent for all submesh vertices
	var v = new Cal3D.CalVector;
	for(var vertexId=0; vertexId<vertexCount; vertexId++) {
		var tangentSpace = vectorTangentSpace[vertexId];

		// get the vertex
		var vertex = vectorVertex[vertexId];

		// initialize tangent
		var tx, ty, tz;
		tx = 0;
		ty = 0;
		tz = 0;

		// blend together all vertex influences
		var influenceCount = vertex.vectorInfluence.length;
		for(var influenceId=0; influenceId<influenceCount; influenceId++) {
			// get the influence
			var influence = vertex.vectorInfluence[influenceId];

			// get the bone of the influence vertex
			var bone = vectorBone[influence.boneId];

			// transform normal with current state of the bone
			v.assign(tangentSpace.tangent);
			v.multMatrixLocal(bone.getTransformMatrix());

			tx += influence.weight * v.x;
			ty += influence.weight * v.y;
			tz += influence.weight * v.z;
		}

		if(!tangentSpaces[vertexId])
			tangentSpaces[vertexId] = new Cal3D.CalCoreSubmesh.TangentSpace;

		// re-normalize tangent if necessary
		if(this.m_normalize) {
			tx /= this.m_axisFactorX;
			ty /= this.m_axisFactorY;
			tz /= this.m_axisFactorZ;

			var scale = 1 / Math.sqrt(tx * tx + ty * ty + tz * tz);

			tangentSpaces[vertexId].tangent.assign(tx * scale, ty * scale, tz * scale);
		}
		else {
			tangentSpaces[vertexId].tangent.assign(tx, ty, tz);
		}

		tangentSpaces[vertexId].crossFactor = tangentSpace.crossFactor;
	}

	return vertexCount;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalSpringSystem
*/
Cal3D.CalSpringSystem = function(model) {
	this.m_model = model;
	this.m_gravity = new Cal3D.CalVector(0, 0, -98.1);
	// we add this force to simulate some movement
	this.m_force = new Cal3D.CalVector(0, 0.5, 0);
	this.m_collision = false;
};

/**
	Calculate the forces upon each unbound vertex.<br />
	This function calculates the forces on each unbound vertex of a specific submesh.
	@param {Cal3D.CalSubmesh} submesh The submesh from which the forces should be calculated.
	@param {number} deltaTime The elapsed time in seconds since the last calculation.
*/
Cal3D.CalSpringSystem.prototype.calculateForces = function(submesh, deltaTime) {
	// get the vertex vector of the submesh
	var vectorVertex = submesh.getVectorVertex();

	// get the vertex vector of the submesh
	var vectorPhysicalProperty = submesh.getVectorPhysicalProperty();

	// get the physical property vector of the core submesh
	var vectorCorePhysicalProperty = submesh.getCoreSubmesh().getVectorPhysicalProperty();

	// loop through all the vertices
	for(var vertexId=0; vertexId<vectorVertex.length; vertexId++) {
		// get the physical property of the vertex
		var physicalProperty = vectorPhysicalProperty[vertexId];

		// get the physical property of the core vertex
		var corePhysicalProperty = vectorCorePhysicalProperty[vertexId];

		// only take vertices with a weight > 0 into account
		if(corePhysicalProperty.weight > 0) {
			// vertex_force = force + gravity * weight
			physicalProperty.force.assign(this.m_gravity);
			physicalProperty.force.multScalarLocal(corePhysicalProperty.weight);
			physicalProperty.force.addLocal(this.m_force);
		}
	}
};

/**
	Calculate the vertices influenced by the spring system instance.<br />
	This function calculates the vertices influenced by the spring system instance.
	@param {Cal3D.CalSubmesh} submesh The submesh from which the vertices should be calculated.
	@param {number} deltaTime The elapsed time in seconds since the last calculation.
*/
Cal3D.CalSpringSystem.prototype.calculateVertices = function(submesh, deltaTime) {
	// get the vertex vector of the submesh
	var vectorVertex = submesh.getVectorVertex();

	// get the physical property vector of the submesh
	var vectorPhysicalProperty = submesh.getVectorPhysicalProperty();

	// get the physical property vector of the core submesh
	var vectorCorePhysicalProperty = submesh.getCoreSubmesh().getVectorPhysicalProperty();

	// loop through all the vertices
	for(var vertexId=0; vertexId<vectorVertex.length; vertexId++) {
		// get the vertex
		var vertex = vectorVertex[vertexId];

		// get the physical property of the vertex
		var physicalProperty = vectorPhysicalProperty[vertexId];

		// get the physical property of the core vertex
		var corePhysicalProperty = vectorCorePhysicalProperty[vertexId];

		// store current position for later use
		var position = new Cal3D.CalVector(physicalProperty.position);

		// only take vertices with a weight > 0 into account
		if(corePhysicalProperty.weight > 0) {
			// do the Verlet step
			physicalProperty.position.addLocal( Cal3D.vectorSub(position, physicalProperty.positionOld).multScalarLocal(0.99).addLocal(Cal3D.vectorScalarMult(physicalProperty.force, deltaTime * deltaTime / corePhysicalProperty.weight)) );

			var skeleton = this.m_model.getSkeleton();

			// collision detection and response
			if(this.m_collision) {
				var vectorBone = skeleton.getVectorBone();

				for(var boneId=0; boneId<vectorBone.length; boneId++) {
					var p = vectorBone[boneId].getBoundingBox();
					var isIn = true;
					var min = 1e10;
					var index = -1;

					for(var faceId=0; faceId<6 ; faceId++) {				
						if(p.plane[faceId].eval(physicalProperty.position) <= 0) {
							isIn = false;
						}
						else {
							var dist = p.plane[faceId].dist(physicalProperty.position);
							if(dist < min) {
								index = faceId;
								min = dist;
							}
						}
					}

					if(isIn && index != -1) {
						var normal = new Cal3D.CalVector(p.plane[index].a, p.plane[index].b, p.plane[index].c);
						normal.normalize();
						physicalProperty.position.subLocal(Cal3D.vectorScalarMult(normal, min));
					}

					isIn = true;

					for(var faceId=0; faceId<6 ; faceId++) {				
						if(p.plane[faceId].eval(physicalProperty.position) < 0 ) {
							isIn = false;				
						}
					}
					if(isIn) {
						physicalProperty.position.assign(vectorVertex[vertexId]);
					}
				}
			}
		}
		else {
			physicalProperty.position.assign(vectorVertex[vertexId]);
		}

		// make the current position the old one
		physicalProperty.positionOld.assign(position);

		// set the new position of the vertex
		vertex.assign(physicalProperty.position);

		// clear the accumulated force on the vertex
		physicalProperty.force.clear();
	}

	// get the spring vector of the core submesh
	var vectorSpring = submesh.getCoreSubmesh().getVectorSpring();

	// iterate a few times to relax the constraints
	var TOTAL_ITERATION_COUNT = 2;
	for(var iterationCount=0; iterationCount<TOTAL_ITERATION_COUNT; iterationCount++) {
		// loop through all the springs
		for(var springId=0; springId<vectorSpring.length; springId++) {
			// get the spring
			var spring = vectorSpring[springId];

			// compute the difference between the two spring vertices
			var distance = Cal3D.vectorSub(vectorVertex[spring.vertexId[1]], vectorVertex[spring.vertexId[0]]);

			// get the current length of the spring
			var length = distance.length();

			if(length > 0) {
				/*
				if (spring.springCoefficient == 0) { 
					vectorVertex[spring.vertexId[1]].assign(vectorVertex[spring.vertexId[0]]);
					vectorPhysicalProperty[spring.vertexId[1]].position.assign(vectorVertex[spring.vertexId[0]]);
				} 
				else {
				*/
				var factor = [0, 0];
				factor[0] = (length - spring.idleLength) / length;
				factor[1] = factor[0];

				if(vectorCorePhysicalProperty[spring.vertexId[0]].weight > 0) {
					factor[0] /= 2;
					factor[1] /= 2;
				}
				else {
					factor[0] = 0;
				}

				if(vectorCorePhysicalProperty[spring.vertexId[1]].weight <= 0) {
					factor[0] *= 2;
					factor[1] = 0;
				}

				vectorVertex[spring.vertexId[0]].addLocal(Cal3D.vectorScalarMult(distance, factor[0]));
				vectorPhysicalProperty[spring.vertexId[0]].position.assign(vectorVertex[spring.vertexId[0]]);

				vectorVertex[spring.vertexId[1]].subLocal(Cal3D.vectorScalarMult(distance, factor[1]));
				vectorPhysicalProperty[spring.vertexId[1]].position.assign(vectorVertex[spring.vertexId[1]]);
				//}
			}
		}
	}
};

/**
	Update all the spring systems in the attached meshes.<br />
	This functon updates all the spring systems in the attached meshes.
	@param {number} deltaTime The elapsed time in seconds since the last calculation.
*/
Cal3D.CalSpringSystem.prototype.update = function(deltaTime) {
	// get the attached meshes vector
	var vectorMesh = this.m_model.getVectorMesh();

	// loop through all the attached meshes
	for(var meshId=0; meshId<vectorMesh.length; meshId++) {
		// get the ssubmesh vector of the mesh
		var vectorSubmesh = vectorMesh[meshId].getVectorSubmesh();

		// loop through all the submeshes of the mesh
		for(var submeshId=0; submeshId<vectorSubmesh.length; submeshId++) {
			var submesh = vectorSubmesh[submeshId];

			// check if the submesh contains a spring system
			if(submesh.getCoreSubmesh().getSpringCount() > 0 && submesh.hasInternalData()) {
				// calculate the new forces on each unbound vertex
				this.calculateForces(submesh, deltaTime);

				// calculate the vertices influenced by the spring system
				this.calculateVertices(submesh, deltaTime);
			}
		}
	}
};

/**
	Get the gravity vector.<br />
	This function returns the gravity vector of the spring system instance.
	@returns {Cal3D.CalVector} The gravity as vector.
*/
Cal3D.CalSpringSystem.prototype.getGravityVector = function() {
	return this.m_gravity;
};

/**
	Set the gravity vector.<br />
	This function sets the gravity vector of the spring system instance.
	@param {Cal3D.CalVector} gravity The gravity as vector.
*/
Cal3D.CalSpringSystem.prototype.setGravityVector = function(gravity) {
	this.m_gravity.assign(gravity);
};

/**
	Get the force vector.<br />
	This function returns the force vector of the spring system instance.
	@returns {Cal3D.CalVector} The force as vector.
*/
Cal3D.CalSpringSystem.prototype.getForceVector = function() {
	return this.m_force;
};

/**
	Set the force vector.<br />
	This function sets the force vector of the spring system instance.
	@param {Cal3D.CalVector} The force as vector.
*/
Cal3D.CalSpringSystem.prototype.setForceVector = function(force) {
	this.m_force.assign(force);
};

/**
	Enable or disable the collision system.<br />
	@param {boolean} collision Set to true to enable the collision system, false to disable the collision system.
*/
Cal3D.CalSpringSystem.prototype.setCollisionDetection = function(collision) {
	this.m_collision = collision;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalRenderer
*/
Cal3D.CalRenderer = function(model) {
	this.m_model = null;
	this.m_selectedSubmesh = null;

	if(arguments[0] instanceof Cal3D.CalModel) {
		this.m_model = arguments[0];
	}
	else if(arguments[0] instanceof Cal3D.CalRenderer) {
		var other = arguments[0];
		this.m_model = other.m_model;
		this.m_selectedSubmesh = other.m_selectedSubmesh;
	}
	else {
		throw 'argument error: input parameter is neither a model instance nor a renderer instance';
	}
};

/**
	Initialize the rendering query phase.<br />
	This function initializes the rendering query phase. It must be called before any rendering queries are executed.
*/
Cal3D.CalRenderer.prototype.beginRendering = function() {
	// get the attached meshes vector
	var vectorMesh = this.m_model.getVectorMesh();

	// check if there are any meshes attached to the model
	if(vectorMesh.length == 0) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'renderer.js');
		return false;
	}

	// select the default submesh
	this.m_selectedSubmesh = vectorMesh[0].getSubmesh(0);
	if(!this.m_selectedSubmesh)
		return false;

	return true;
};

/**
	Finish the rendering query phase.<br />
	This function finishes the rendering query phase. It must be called after all rendering queries have been executed.
*/
Cal3D.CalRenderer.prototype.endRendering = function() {
	// clear selected submesh
	this.m_selectedSubmesh = null;
};

/**
	Get the ambient color.<br />
	This function returns the ambient color of the material of the selected mesh/submesh.
	@param {Array} colorBuffer The user-provided buffer where the color data is written to. This is an output parameter.
*/
Cal3D.CalRenderer.prototype.getAmbientColor = function(colorBuffer) {
	// get the core material
	var coreMaterial = this.m_model.getCoreModel().getCoreMaterial(this.m_selectedSubmesh.getCoreMaterialId());
	if(!coreMaterial) {
		// write default values to the color buffer
		colorBuffer[0] = 0;
		colorBuffer[1] = 0;
		colorBuffer[2] = 0;
		colorBuffer[3] = 0;

		return;
	}

	// get the ambient color of the material
	var color = coreMaterial.getAmbientColor();

	// write it to the color buffer
	colorBuffer[0] = color.red;
	colorBuffer[1] = color.green;
	colorBuffer[2] = color.blue;
	colorBuffer[3] = color.alpha;
};

/**
	Get the diffuse color.<br />
	This function returns the diffuse color of the material of the selected mesh/submesh.
	@param {Array} colorBuffer The user-provided buffer where the color data is written to. This is an output parameter.
*/
Cal3D.CalRenderer.prototype.getDiffuseColor = function(colorBuffer) {
	// get the core material
	var coreMaterial = this.m_model.getCoreModel().getCoreMaterial(this.m_selectedSubmesh.getCoreMaterialId());
	if(!coreMaterial) {
		// write default values to the color buffer
		colorBuffer[0] = 192;
		colorBuffer[1] = 192;
		colorBuffer[2] = 192;
		colorBuffer[3] = 192;

		return;
	}

	// get the diffuse color of the material
	var color = coreMaterial.getDiffuseColor();

	// write it to the color buffer
	colorBuffer[0] = color.red;
	colorBuffer[1] = color.green;
	colorBuffer[2] = color.blue;
	colorBuffer[3] = color.alpha;
};

/**
	Get the number of faces.<br />
	This function returns the number of faces in the selected mesh/submesh.
	@returns {number} The number of faces.
*/
Cal3D.CalRenderer.prototype.getFaceCount = function() {
	return this.m_selectedSubmesh.getFaceCount();
};

/**
	Get the face data.<br />
	This function returns the face data (vertex indices) of the selected mesh/submesh. The LOD setting is taken into account.
	@param {Array} faceBuffer The user-provided buffer where the face data is written to. This is an output parameter.
	@returns {number} The number of faces written to the buffer.
*/
Cal3D.CalRenderer.prototype.getFaces = function(faceBuffer) {
	return this.m_selectedSubmesh.getFaces(faceBuffer);
};

/**
	Get the number of texture maps.<br />
	This function returns the number of texture maps in the selected mesh/submesh.
	@returns {number} The number of texture maps.
*/
Cal3D.CalRenderer.prototype.getMapCount = function() {
	// get the core material
	var coreMaterial = this.m_model.getCoreModel().getCoreMaterial(this.m_selectedSubmesh.getCoreMaterialId());
	if(!coreMaterial)
		return 0;

	return coreMaterial.getMapCount();
};

/**
	Get a specified texture map's user data.<br />
	This function returns the user data stored in the specified texture map of the material of the selected mesh/submesh.
	@param {number} mapId The ID of the texture map.
	@returns {object} The user data stored in the specified texture map; null if no user data is stored or any error happened.
*/
Cal3D.CalRenderer.prototype.getMapUserData = function(mapId) {
	// get the core material
	var coreMaterial = this.m_model.getCoreModel().getCoreMaterial(this.m_selectedSubmesh.getCoreMaterialId());
	if(!coreMaterial)
		return null;

	// get the map vector
	var vectorMap = coreMaterial.getVectorMap();

	// check if the map id is valid
	if(mapId < 0 || mapId >= vectorMap.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'renderer.js');
		return null;
	}

	return vectorMap[mapId].userData;
};

/**
	Get the number of attached meshes.<br />
	This function returns the number of meshes attached to the renderer instance.
	@returns {number} The number of attached meshes.
*/
Cal3D.CalRenderer.prototype.getMeshCount = function() {
	// get the attached meshes vector
	var vectorMesh = this.m_model.getVectorMesh();
	return vectorMesh.length;
};

/**
	Get the normal data.<br />
	This function returns the vertex normal data of the selected mesh/submesh.
	@param {Array} normalBuffer The user-provided buffer where the normal data is written to. This is an output parameter.
	@param {number} stride (Optional). The offset from the start of one data to the start of next data, default to 3.
	@returns {number} The number of normals written to the buffer.
*/
Cal3D.CalRenderer.prototype.getNormals = function(normalBuffer, stride) {
	// check if the submesh handles vertex data internally
	if(this.m_selectedSubmesh.hasInternalData()) {
		// get the normal vector of the submesh
		var vectorNormal = this.m_selectedSubmesh.getVectorNormal();

		// get the number of normals (= number of vertices) in the submesh
		var normalCount = this.m_selectedSubmesh.getVertexCount();

		/*
			copy the internal normal data to the provided normal buffer
		*/
		if(stride == undefined || stride <= 0)
			stride = 3;

		for(var i=0, j=0; i<normalCount; i++, j+=stride) {
			var normal = vectorNormal[i];
			normalBuffer[j    ] = normal.x;
			normalBuffer[j + 1] = normal.y;
			normalBuffer[j + 2] = normal.z;
		}

		return normalCount;
	}

	// submesh does not handle the vertex data internally, so let the physique calculate it now
	return this.m_model.getPhysique().calculateNormals(this.m_selectedSubmesh, normalBuffer, stride);
};

/**
	Get the shininess factor.<br />
	This function returns the shininess factor of the material of the selected mesh/submesh.
	@returns {number} The shininess factor.
*/
Cal3D.CalRenderer.prototype.getShininess = function() {
	// get the core material
	var coreMaterial = this.m_model.getCoreModel().getCoreMaterial(this.m_selectedSubmesh.getCoreMaterialId());
	if(!coreMaterial)
		return 50;

	return coreMaterial.getShininess();
};

/**
	Get the specular color.<br />
	This function returns the specular color of the material of the selected mesh/submesh.
	@param {Array} colorBuffer The user-provided buffer where the color data is written to. This is an output parameter.
*/
Cal3D.CalRenderer.prototype.getSpecularColor = function(colorBuffer) {
	// get the core material
	var coreMaterial = this.m_model.getCoreModel().getCoreMaterial(this.m_selectedSubmesh.getCoreMaterialId());
	if(!coreMaterial) {
		// write default values to the color buffer
		colorBuffer[0] = 255;
		colorBuffer[1] = 255;
		colorBuffer[2] = 255;
		colorBuffer[3] = 0;

		return;
	}

	// get the specular color of the material
	var color = coreMaterial.getSpecularColor();

	// write it to the color buffer
	colorBuffer[0] = color.red;
	colorBuffer[1] = color.green;
	colorBuffer[2] = color.blue;
	colorBuffer[3] = color.alpha;
};

/**
	Get the number of submeshes.<br />
	This function returns the number of submeshes in a given mesh.
	@param {number} meshId The ID of the mesh whose number of submeshes should be returned.
	@returns {number} The number of submeshes.
*/
Cal3D.CalRenderer.prototype.getSubmeshCount = function(meshId) {
	// get the attached meshes vector
	var vectorMesh = this.m_model.getVectorMesh();

	// check if the mesh id is valid
	if(meshId < 0 || meshId >= vectorMesh.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'renderer.js');
		return 0;
	}

	return vectorMesh[meshId].getSubmeshCount();
};

/**
	Get the texture coordinate data.<br />
	This function returns the texture coordinate data for a given map of the selected mesh/submesh.
	@param {number} mapId The ID of the map to get the texture coordinate data from.
	@param {Array} textureCoordinateBuffer The user-provided buffer where the texture coordinate data is written to. This is an output parameter.
	@param {number} stride (Optional). The offset from the start of one data to the start of next data, default to 2.
	@returns {number} The number of texture coordinates written to the buffer.
*/
Cal3D.CalRenderer.prototype.getTextureCoordinates = function(mapId, textureCoordinateBuffer, stride) {
	// get the texture coordinate vector vector
	var vectorvectorTextureCoordinate = this.m_selectedSubmesh.getCoreSubmesh().getVectorVectorTextureCoordinate();

	// check if the map id is valid
	if(mapId < 0 || mapId >= vectorvectorTextureCoordinate.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'renderer.js');
		return -1;
	}

	// get the number of texture coordinates to return
	var textureCoordinateCount = this.m_selectedSubmesh.getVertexCount();

	/*
		copy the texture coordinate data to the provided texture coordinate buffer
	*/
	if(stride == undefined || stride <= 0)
		stride = 2;

	for(var i=0, j=0; i<textureCoordinateCount; i++, j+=stride) {
		var texCoord = vectorvectorTextureCoordinate[mapId][i];
		textureCoordinateBuffer[j    ] = texCoord.u;
		textureCoordinateBuffer[j + 1] = texCoord.v;
	}

	return textureCoordinateCount;
};

/**
	Get the number of vertices.<br />
	This function returns the number of vertices in the selected mesh/submesh.
	@returns {number} The number of vertices.
*/
Cal3D.CalRenderer.prototype.getVertexCount = function() {
	return this.m_selectedSubmesh.getVertexCount();
};

/**
	Get the vertex data.<br />
	This function returns the vertex data of the selected mesh/submesh.
	@param {Array} vertexBuffer The user-provided buffer where the vertex data is written to. This is an output parameter.
	@param {number} stride (Optional). The offset from the start of one data to the start of next data, default to 3.
	@returns {number} The number of vertices written to the buffer.
*/
Cal3D.CalRenderer.prototype.getVertices = function(vertexBuffer, stride) {
	// check if the submesh handles vertex data internally
	if(this.m_selectedSubmesh.hasInternalData()) {
		// get the vertex vector of the submesh
		var vectorVertex = this.m_selectedSubmesh.getVectorVertex();

		// get the number of vertices in the submesh
		var vertexCount = this.m_selectedSubmesh.getVertexCount();

		// copy the internal vertex data into the provided vertex buffer

		if(stride == undefined || stride <= 0)
			stride = 3;

		for(var i=0, j=0; i<vertexCount; i++, j+=stride) {
			var vertex = vectorVertex[i];
			vertexBuffer[j    ] = vertex.x;
			vertexBuffer[j + 1] = vertex.y;
			vertexBuffer[j + 2] = vertex.z;
		}

		return vertexCount;
	}

	// submesh does not handle the vertex data internally, so let the physique calculate it now
	return this.m_model.getPhysique().calculateVertices(this.m_selectedSubmesh, vertexBuffer, stride);
};

/**
	Get the tangent space data.<br />
	This function returns the tangent space data of the selected mesh/submesh.
	@param {number} mapId The ID of the texture map.
	@param {Array} tangentSpaceBuffer The user-provided buffer where the tangent space ata is written to. This is an output parameter.
	@param {number} stride (Optional). The offset from the start of one data to the start of next data, default to 4.
	@returns {number} The number of tangent space written to the buffer.
*/
Cal3D.CalRenderer.prototype.getTangentSpaces = function(mapId, tangentSpaceBuffer, stride) {
	// get the texture coordinate vector vector
	var vectorvectorTangentSpace = this.m_selectedSubmesh.getCoreSubmesh().getVectorVectorTangentSpace();

	// check if the map id is valid
	if(mapId < 0 || mapId >= vectorvectorTangentSpace.length || !this.m_selectedSubmesh.isTangentsEnabled(mapId)) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'renderer.js');
		return -1;
	}

	// check if the submesh handles vertex data internally
	if(this.m_selectedSubmesh.hasInternalData()) {
		// get the normal vector of the submesh
		var vectorTangentSpace = this.m_selectedSubmesh.getVectorVectorTangentSpace()[mapId];

		// get the number of normals (= number of vertices) in the submesh
		var tangentSpaceCount = this.m_selectedSubmesh.getVertexCount();

		/* 
			copy the internal normal data to the provided tangent space buffer
		*/
		if(stride == undefined || stride <= 0)
			stride = 4;

		for(var i=0, j=0; i<tangentSpaceCount; i++, j+=stride) {
			var tangentSpace = vectorTangentSpace[i];
			var tangent = tangentSpace.tangent;
			tangentSpaceBuffer[j    ] = tangent.x;
			tangentSpaceBuffer[j + 1] = tangent.y;
			tangentSpaceBuffer[j + 2] = tangent.z;
			tangentSpaceBuffer[j + 3] = tangentSpace.crossFactor;
		}

		return tangentSpaceCount;
	}

	// submesh does not handle the vertex data internally, so let the physique calculate it now
	return this.m_model.getPhysique().calculateTangentSpaces(this.m_selectedSubmesh, mapId, tangentSpaceBuffer, stride);
};

/**
	Get the vertex and normal data.<br />
	This function returns the vertex and vertex normal data of the selected mesh/submesh.
	@param {Array} vertexBuffer The user-provided buffer where the vertex and normal data is written to. This is an output parameter.
	@param {number} stride (Optional). The offset from the start of one data to the start of next data, default to 6.
	@returns {number} The number of vertices written to the buffer.
*/
Cal3D.CalRenderer.prototype.getVerticesAndNormals = function(vertexBuffer, stride) {
	// check if the submesh handles vertex data internally
	if(this.m_selectedSubmesh.hasInternalData()) {
		// get the vertex vector of the submesh
		var vectorVertex = this.m_selectedSubmesh.getVectorVertex();
		// get the normal vector of the submesh
		var vectorNormal = this.m_selectedSubmesh.getVectorNormal();

		// get the number of vertices in the submesh
		var vertexCount = this.m_selectedSubmesh.getVertexCount();

		/*
			copy the internal vertex and normal data to the provided vertex buffer
		*/
		if(stride == undefined || stride <= 0)
			stride = 6;

		for(i=0, j=0; i<vertexCount; i++, j+=stride) {
			var vertex = vectorVertex[i];
			var normal = vectorNormal[i];
			vertexBuffer[j    ] = vertex.x;
			vertexBuffer[j + 1] = vertex.y;
			vertexBuffer[j + 2] = vertex.z;
			vertexBuffer[j + 3] = normal.x;
			vertexBuffer[j + 4] = normal.y;
			vertexBuffer[j + 5] = normal.z;
		}

		return vertexCount;
	}

	// submesh does not handle the vertex data internally, so let the physique calculate it now
	return this.m_model.getPhysique().calculateVerticesAndNormals(this.m_selectedSubmesh, vertexBuffer, stride);
};

/**
	Get the vertex, normal and texture coordinate data.<br />
	This function returns the vertex, vertex normal and texture coordinate data of the selected mesh/submesh.
	@param {Array} vertexBuffer The user-provided buffer where the vertex, normal and texture coordinate data is written to. This is an output parameter.
	@param {number} numTexCoords The number of texture maps whose texture coordinates is required, since there can be 0 or more than 1 texture maps defined.
	@returns {number} The number of vertices written to the buffer.
*/
Cal3D.CalRenderer.prototype.getVerticesNormalsAndTexCoords = function(vertexBuffer, numTexCoords) {
	// check if the submesh handles vertex data internally
	if(this.m_selectedSubmesh.hasInternalData()) {
		// get the vertex vector of the submesh
		var vectorVertex = this.m_selectedSubmesh.getVectorVertex();
		// get the normal vector of the submesh
		var vectorNormal = this.m_selectedSubmesh.getVectorNormal();	
		// get the texture coordinate vector vector
		var vectorvectorTextureCoordinate = this.m_selectedSubmesh.getCoreSubmesh().getVectorVectorTextureCoordinate();

		var textureCoordinateCount = vectorvectorTextureCoordinate.length;

		if(numTexCoords == undefined)
			numTexCoords = 1;

		// check if the given texture coordinate count is valid
		if(numTexCoords < 0 || numTexCoords > textureCoordinateCount) {
			if(textureCoordinateCount != 0) {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'renderer.js');
				return -1;
			}
		}

		// get the number of vertices in the submesh
		var vertexCount = this.m_selectedSubmesh.getVertexCount();

		/*
			copy the internal vertex, normal and texture coordinate data to the provided vertex buffer
		*/

		if(textureCoordinateCount == 0) {
			var vi = 0;
			for(var vertexId=0; vertexId<vertexCount; vertexId++) {
				var vertex = vectorVertex[vertexId];
				var normal = vectorNormal[vertexId];
				vertexBuffer[vi    ] = vertex.x;
				vertexBuffer[vi + 1] = vertex.y;
				vertexBuffer[vi + 2] = vertex.z;
				vertexBuffer[vi + 3] = normal.x;
				vertexBuffer[vi + 4] = normal.y;
				vertexBuffer[vi + 5] = normal.z;
				vi += 6 + numTexCoords * 2;
			}
		}
		else
		{
			if(numTexCoords == 1) {
				var vi = 0;
				for(var vertexId=0; vertexId<vertexCount; vertexId++) {
					var vertex = vectorVertex[vertexId];
					var normal = vectorNormal[vertexId];
					var texCoord = vectorvectorTextureCoordinate[0][vertexId];
					vertexBuffer[vi    ] = vertex.x;
					vertexBuffer[vi + 1] = vertex.y;
					vertexBuffer[vi + 2] = vertex.z;
					vertexBuffer[vi + 3] = normal.x;
					vertexBuffer[vi + 4] = normal.y;
					vertexBuffer[vi + 5] = normal.z;
					vertexBuffer[vi + 6] = texCoord.u;
					vertexBuffer[vi + 7] = texCoord.v;
					vi += 8;
				}
			}
			else
			{
				var vi = 0;
				for(var vertexId=0; vertexId<vertexCount; vertexId++) {
					var vertex = vectorVertex[vertexId];
					var normal = vectorNormal[vertexId];
					vertexBuffer[vi    ] = vertex.x;
					vertexBuffer[vi + 1] = vertex.y;
					vertexBuffer[vi + 2] = vertex.z;
					vertexBuffer[vi + 3] = normal.x;
					vertexBuffer[vi + 4] = normal.y;
					vertexBuffer[vi + 5] = normal.z;
					vi += 6;

					for(var mapId=0; mapId<numTexCoords; mapId++) {
						var texCoord = vectorvectorTextureCoordinate[mapId][vertexId];
						vertexBuffer[vi    ] = texCoord.u;
						vertexBuffer[vi + 1] = texCoord.v;
						vi += 2;
					}
				}
			}		
		}

		return vertexCount;
	}

	// submesh does not handle the vertex data internally, so let the physique calculate it now
	return this.m_model.getPhysique().calculateVerticesNormalsAndTexCoords(this.m_selectedSubmesh, vertexBuffer, numTexCoords);
};

/**
	See if tangent are enabled on a specific texture map.<br />
	This function returns if tangent of the current submesh are enabled.
	@param {number} mapId The ID of the texture map.
	@returns {boolean} Whether tangent is enabled.
*/
Cal3D.CalRenderer.prototype.isTangentsEnabled = function(mapId) {
	return this.m_selectedSubmesh.isTangentsEnabled(mapId);
};

/**
	Select a mesh/submesh for rendering data queries.<br />
	This function selects a mesh/submesh for further rendering data queries.
	@param {number} meshId The ID of the mesh that should be used for further rendering data queries.
	@param {number} submeshId The ID of the submesh that should be used for further rendering data queries.
	@returns {boolean} true if succeeded; false if the mesh or the submesh does not exist.
*/
Cal3D.CalRenderer.prototype.selectMeshSubmesh = function(meshId, submeshId) {
	// get the attached meshes vector
	var vectorMesh = this.m_model.getVectorMesh();

	// check if the mesh id is valid
	if(meshId < 0 || meshId >= vectorMesh.length) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_HANDLE, 'renderer.js');
		return false;
	}

	// get the core submesh
	this.m_selectedSubmesh = vectorMesh[meshId].getSubmesh(submeshId);
	if(!this.m_selectedSubmesh)
		return false;

	return true;
};

/**
	Set the normalization flag.<br />
	This function sets the normalization flag on or off. If off, the normals calculated by Cal3D will not be normalized. Instead, 
	this transform is left up to the user.
	@param {boolean} normalize The normalization flag.
*/
Cal3D.CalRenderer.prototype.setNormalization = function(normalize) {
	this.m_model.getPhysique().setNormalization(normalize);
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@namespace CalLibraryConstants
*/
Cal3D.CalLibraryConstants = {
	// binary file magic cookies
	SKELETON_FILE_MAGIC:		'CSF', 
	ANIMATION_FILE_MAGIC:		'CAF', 
	MESH_FILE_MAGIC:			'CMF', 
	MATERIAL_FILE_MAGIC:		'CRF', 

	// xml file magic cookies
	SKELETON_XMLFILE_MAGIC:		'XSF', 
	ANIMATION_XMLFILE_MAGIC:	'XAF', 
	MESH_XMLFILE_MAGIC:			'XMF', 
	MATERIAL_XMLFILE_MAGIC:		'XRF', 

	// library version 
	LIBRARY_VERSION:					1100,	// 0.11.0

	// file versions
	CURRENT_FILE_VERSION:				1100,	// should be the same as LIBRARY_VERSION
	EARLIEST_COMPATIBLE_FILE_VERSION:	699		// the earliest file version that is acceptable
};



/**
	@class CalLoadingCallback

	The loader callback interface.
*/
Cal3D.CalLoadingCallback = function() {
	this.model = null;
	this.error = '';
	this.finished = false;
};

Cal3D.CalLoadingCallback.prototype.onload = function(object, url) {
	this.model = object.model;
	print('LOADED MODEL');
};

Cal3D.CalLoadingCallback.prototype.onerror = function(code, url) {
	this.error = 'Error: '+code;
	print('Error'+code);
};

Cal3D.CalLoadingCallback.prototype.onprogress = function(percent, url) {
	print('P:'+percent);
};

Cal3D.CalLoadingCallback.prototype.isfinished = function() {
	return this.finished;
};

Cal3D.CalLoadingCallback.prototype.getmodel = function() {
	return this.model;
};

Cal3D.CalLoadingCallback.prototype.geterror = function() {
	return this.error;
};




/**
	@namespace CalLoader
*/
Cal3D.CalLoader = {};

Cal3D.CalLoader.LOADER_ROTATE_X_AXIS	= 0x01;
Cal3D.CalLoader.LOADER_INVERT_V_COORD	= 0x02;
Cal3D.CalLoader.LOADER_FLIP_WINDING		= 0x04;

/**
	Set optional flags which affect how the model is loaded for all future loader calls.<br />
	@param {number} flags Can be any combination of the following flags: 
		<li><b> Cal3D.CalLoader.LOADER_ROTATE_X_AXIS </b></li> will rotate the mesh 90 degrees about the X axis which has the effect of swapping Y/Z coordinates.
		<li><b> Cal3D.CalLoader.LOADER_INVERT_V_COORD </b></li> will substitute (1-v) for any v texture coordinate to eliminate the need for texture inversion after export.
		<li><b> Cal3D.CalLoader.LOADER_FLIP_WINDING </b></li> will flip the winding for all faces.
*/
Cal3D.CalLoader.setLoadingMode = function(flags) {
	Cal3D.CalLoader.loadingMode = flags;
};

/**
	Load an entire Cal3D model from a given config file.<br />
	@param {string} url The url of the config file.
	@param {object} callback An object which implements the loader callback interface to retrieve the modle loaded and/or the error information and/or the loading progress information.
*/
Cal3D.CalLoader.loadModelFromConfigFile = function(url, callback) {
	var xhr = new XMLHttpRequest;
	
	xhr.open('GET', url, true);
	xhr.overrideMimeType('text/plain; charset=x-user-defined');

	xhr.onreadystatechange = function() {
		if(this.readyState == 4) {
			if(this.status == 200 || this.status == 0) {
				var cfg = Cal3D.CalLoader.parseConfigFile(this.responseText);

				if(cfg.skeletons.length == 0) {
					Cal3D.CalError.setLastError(Cal3D.CalError.Code.FILE_NOT_FOUND, 'loader.js');
					if(callback && callback.onerror) {
						print('Error');
						callback.onerror(Cal3D.CalError.Code.FILE_NOT_FOUND, 'skeleton file');
					}
					return;
				}
				print('Got skeletons 1: '+' anims: '+cfg.animations.length+' meshes: '+cfg.meshes.length+' materials: '+cfg.materials.length);
				var resourceCount = 1 /* the one and only skeleton file */ + cfg.animations.length + cfg.meshes.length + cfg.materials.length;
				var resources = {
					skeletons: {}, 
					animations: {}, 
					meshes: {}, 
					materials: {}
				};

				
					// create a new core model instance
					var coreModel = new Cal3D.CalCoreModel('dummy');

					// set core skeleton
					coreModel.setCoreSkeleton(resources.skeletons[cfg.path + cfg.skeletons[0]]);
					print('Set skeleton');
					// set core animations
					for(var i=0; i<cfg.animations.length; i++) {
						coreModel.addCoreAnimation(resources.animations[cfg.path + cfg.animations[i]]);
					}
					print('Set Animations');
					// set core meshes
					for(var i=0; i<cfg.meshes.length; i++) {
						coreModel.addCoreMesh(resources.meshes[cfg.path + cfg.meshes[i]]);
					}
					print('Set Meshes');
					// set core materials
					for(var i=0; i<cfg.materials.length; i++) {
						coreModel.addCoreMaterial(resources.materials[cfg.path + cfg.materials[i]]);
					}
					print('Set Materials');
					// make one material thread for each material
					// FIXME: maybe this is not the right way to do it, while this is the only thing 
					//        we can do here as we haven't got any further information about the model.
					for(var materialId=0; materialId<cfg.materials.length; materialId++) {
						// create the a material thread
						coreModel.createCoreMaterialThread(materialId);

						// initialize the material thread
						coreModel.setCoreMaterialId(materialId, 0, materialId);
					}
					print('Start Calculate Bounding Boxes');
					// calculate bounding boxes
					coreModel.getCoreSkeleton().calculateBoundingBoxes(coreModel);
					print('Calculated Bounding Boxes');
					// create a new model instance
					var model = new Cal3D.CalModel(coreModel);
					print('Made new Model');
					// attach all meshes to the model
					for(var meshId=0; meshId<cfg.meshes.length; meshId++) {
						model.attachMesh(meshId);
					}

					// set the material set of the whole model
					model.setMaterialSet(0);
					
					if(callback && callback.onload) {
						callback.onload({ model: model, path: cfg.path, scale: cfg.scale }, url);
					}
					print('Add model to Callback');

				var animationCB = {};
				animationCB.onload = function(coreAnimation, url) {
					resources.animations[url] = coreAnimation;
					if(--resourceCount == 0)
						setupModel();
				};
				animationCB.onerror = function(code, url) {
					if(callback && callback.onerror) {
						callback.onerror(code, url);
					}
				};

				var meshCB = {};
				meshCB.onload = function(coreMesh, url) {
					resources.meshes[url] = coreMesh;
					if(--resourceCount == 0)
						setupModel();
				};
				meshCB.onerror = function(code, url) {
					if(callback && callback.onerror) {
						callback.onerror(code, url);
					}
				};

				var materialCB = {};
				materialCB.onload = function(coreMaterial, url) {
					resources.materials[url] = coreMaterial;
					if(--resourceCount == 0)
						setupModel();
				};
				materialCB.onerror = function(code, url) {
					if(callback && callback.onerror) {
						callback.onerror(code, url);
					}
				};

				var skeletonCB = {};
				skeletonCB.onload = function(coreSkeleton, url) {
					resources.skeletons[url] = coreSkeleton;

					for(var i=0; i<cfg.animations.length; i++) {
						Cal3D.CalLoader.loadCoreAnimationFromFile(cfg.path + cfg.animations[i], coreSkeleton, animationCB);
					}

					for(var i=0; i<cfg.meshes.length; i++) {
						Cal3D.CalLoader.loadCoreMeshFromFile(cfg.path + cfg.meshes[i], meshCB);
					}

					for(var i=0; i<cfg.materials.length; i++) {
						Cal3D.CalLoader.loadCoreMaterialFromFile(cfg.path + cfg.materials[i], materialCB);
					}

					if(--resourceCount == 0)
						setupModel();
				};
				skeletonCB.onerror = function(code, url) {
					if(callback && callback.onerror) {
						callback.onerror(code, url);
					}
				};

				Cal3D.CalLoader.loadCoreSkeletonFromFile(cfg.path + cfg.skeletons[0], skeletonCB);
			}
		}
	};

	xhr.onerror = function() {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.FILE_NOT_FOUND, 'loader.js', 0, url);
		if(callback && callback.onerror) {
			callback.onerror(Cal3D.CalError.Code.FILE_NOT_FOUND, url);
		}
	};

	xhr.onprogress = function(evt) {
		if(callback && callback.onprogress) {
			callback.onprogress(evt.position / evt.totalSize, url);
		}
	};

	xhr.send();
};

/**
Sync loader
*/

Cal3D.CalLoader.sloadModelFromConfigFile = function(cfg) {


	
		
			
				

				if(cfg.skeletons.length == 0) {
					print('No Skeleton');
				}

				var resourceCount = 1 /* the one and only skeleton file */ + cfg.animations.length + cfg.meshes.length + cfg.materials.length;
				var resources = {
					skeletons: {}, 
					animations: {}, 
					meshes: {}, 
					materials: {}
				};

				
					// create a new core model instance
					var coreModel = new Cal3D.CalCoreModel('dummy');
					print('Skeleton: '+cfg.path + cfg.skeletons[0]);
					// set core skeleton
					coreModel.setCoreSkeleton(resources.skeletons[cfg.path + cfg.skeletons[0]]);

					// set core animations
					for(var i=0; i<cfg.animations.length; i++) {
						coreModel.addCoreAnimation(resources.animations[cfg.path + cfg.animations[i]]);
					}

					// set core meshes
					for(var i=0; i<cfg.meshes.length; i++) {
						coreModel.addCoreMesh(resources.meshes[cfg.path + cfg.meshes[i]]);
					}

					// set core materials
					for(var i=0; i<cfg.materials.length; i++) {
						coreModel.addCoreMaterial(resources.materials[cfg.path + cfg.materials[i]]);
					}

					// make one material thread for each material
					// FIXME: maybe this is not the right way to do it, while this is the only thing 
					//        we can do here as we haven't got any further information about the model.
					for(var materialId=0; materialId<cfg.materials.length; materialId++) {
						// create the a material thread
						coreModel.createCoreMaterialThread(materialId);

						// initialize the material thread
						coreModel.setCoreMaterialId(materialId, 0, materialId);
					}

					// calculate bounding boxes
					coreModel.getCoreSkeleton().calculateBoundingBoxes(coreModel);

					// create a new model instance
					var model = new Cal3D.CalModel(coreModel);

					// attach all meshes to the model
					for(var meshId=0; meshId<cfg.meshes.length; meshId++) {
						model.attachMesh(meshId);
					}

					// set the material set of the whole model
					model.setMaterialSet(0);

					

				

				var animationCB = {};
				animationCB.onload = function(coreAnimation, url) {
					resources.animations[url] = coreAnimation;
					if(--resourceCount == 0)
						setupModel();
				};
				animationCB.onerror = function(code, url) {
					if(callback && callback.onerror) {
						callback.onerror(code, url);
					}
				};

				var meshCB = {};
				meshCB.onload = function(coreMesh, url) {
					resources.meshes[url] = coreMesh;
					if(--resourceCount == 0)
						setupModel();
				};
				meshCB.onerror = function(code, url) {
					if(callback && callback.onerror) {
						callback.onerror(code, url);
					}
				};

				var materialCB = {};
				materialCB.onload = function(coreMaterial, url) {
					resources.materials[url] = coreMaterial;
					if(--resourceCount == 0)
						setupModel();
				};
				materialCB.onerror = function(code, url) {
					if(callback && callback.onerror) {
						callback.onerror(code, url);
					}
				};

				var skeletonCB = {};
				skeletonCB.onload = function(coreSkeleton, url) {
					resources.skeletons[url] = coreSkeleton;

					for(var i=0; i<cfg.animations.length; i++) {
						Cal3D.CalLoader.loadCoreAnimationFromFile(cfg.path + cfg.animations[i], coreSkeleton, animationCB);
					}

					for(var i=0; i<cfg.meshes.length; i++) {
						Cal3D.CalLoader.loadCoreMeshFromFile(cfg.path + cfg.meshes[i], meshCB);
					}

					for(var i=0; i<cfg.materials.length; i++) {
						Cal3D.CalLoader.loadCoreMaterialFromFile(cfg.path + cfg.materials[i], materialCB);
					}

					if(--resourceCount == 0)
						setupModel();
				};
				skeletonCB.onerror = function(code, url) {
					if(callback && callback.onerror) {
						callback.onerror(code, url);
					}
				};

				Cal3D.CalLoader.loadCoreSkeletonFromFile(cfg.path + cfg.skeletons[0], skeletonCB);
			
				return model;
	


};

/**
	Load a core animation instance from a given url.<br />
	@param {string} url The url where to load the core animation instance.
	@param {Cal3D.CalCoreSkeleton} skel The core skeleton instance the core animation is attached to. This parameter is optional.
	@param {object} callback An object which implements the loader callback interface to retrieve the core animation instance loaded and/or the error information and/or the loading progress information.
*/
Cal3D.CalLoader.loadCoreAnimationFromFile = function(url, skel, callback) {
	if( url.length >= 3 && 
		url.substring(url.length - 3, url.length).toUpperCase() == Cal3D.CalLibraryConstants.ANIMATION_XMLFILE_MAGIC ) {
		Cal3D.CalLoader.loadXmlCoreAnimation(url, skel, callback);
		return;
	}

	var xhr = new XMLHttpRequest;
	xhr.open('GET', url, true);
	xhr.overrideMimeType('text/plain; charset=x-user-defined');

	xhr.onreadystatechange = function() {
		if(this.readyState == 4) {
			if(this.status == 200 || this.status == 0) {
				var coreAnim = Cal3D.CalLoader.loadCoreAnimationFromData(this.responseText, skel);
				if(coreAnim) {
					coreAnim.setFilename(url);
					if(callback && callback.onload) {
						callback.onload(coreAnim, url);
					}
				}
				else if(callback && callback.onerror) {
					callback.onerror(Cal3D.CalError.Code.INVALID_FILE_FORMAT, url);
				}
			}
		}
	};

	xhr.onerror = function() {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.FILE_NOT_FOUND, 'loader.js', 0, url);
		if(callback && callback.onerror) {
			callback.onerror(Cal3D.CalError.Code.FILE_NOT_FOUND, url);
		}
	};

	xhr.onprogress = function(evt) {
		if(callback && callback.onprogress) {
			callback.onprogress(evt.position / evt.totalSize, url);
		}
	};

	xhr.send();
};

/**
	Load a core animation instance from a given data buffer.<br />
	@param {string} data The data buffer from which to load the core animation instance.
	@param {Cal3D.CalCoreSkeleton} skel The core skeleton instance the core animation is attached to. This parameter is optional.
	@returns {Cal3D.CalCoreAnimation} The core animation instance loaded; null if any error happened.
*/
Cal3D.CalLoader.loadCoreAnimationFromData = function(data, skel) {
	// check if this is a valid animation file
	if(data.substring(0, 3) != Cal3D.CalLibraryConstants.ANIMATION_FILE_MAGIC) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	var datasource = new Cal3D.CalBufferSource(data.substring(4, data.length));
	
	// check if the version is compatible with the library
	var version = datasource.readInteger();
	if( isNaN(version) || version < Cal3D.CalLibraryConstants.EARLIEST_COMPATIBLE_FILE_VERSION || 
		version > Cal3D.CalLibraryConstants.CURRENT_FILE_VERSION ) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INCOMPATIBLE_FILE_VERSION, 'loader.js');
		return null;
	}

	// create a new core animation instance
	var coreAnimation = new Cal3D.CalCoreAnimation;

	// get the duration of the core animation
	var duration = datasource.readFloat();
	if(isNaN(duration)) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	// check for a valid duration
	if(duration <= 0) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_ANIMATION_DURATION, 'loader.js');
		return null;
	}

	// set the duration in the core animation instance
	coreAnimation.setDuration(duration);

	// read the number of tracks
	var trackCount = datasource.readInteger();
	if(isNaN(trackCount) || trackCount <= 0) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	// load all core tracks
	for(var trackId=0; trackId<trackCount; trackId++) {
		// load the core track
		var coreTrack = Cal3D.CalLoader.loadCoreTrack(datasource, skel, duration);
		if(!coreTrack) {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			return null;
		}

		// add the core track to the core animation instance
		coreAnimation.addCoreTrack(coreTrack);
	}

	return coreAnimation;
};

/**
	Load a core material instance from a given url.<br />
	@param {string} url The url where to load the core material instance.
	@param {object} callback An object which implements the loader callback interface to retrieve the core material instance loaded and/or the error information and/or the loading progress information.
*/
Cal3D.CalLoader.loadCoreMaterialFromFile = function(url, callback) {
	if( url.length >= 3 && 
		url.substring(url.length - 3, url.length).toUpperCase() == Cal3D.CalLibraryConstants.MATERIAL_XMLFILE_MAGIC ) {
		Cal3D.CalLoader.loadXmlCoreMaterial(url, callback);
		return;
	}

	var xhr = new XMLHttpRequest;
	xhr.open('GET', url, true);
	xhr.overrideMimeType('text/plain; charset=x-user-defined');

	xhr.onreadystatechange = function() {
		if(this.readyState == 4) {
			if(this.status == 200 || this.status == 0) {
				var coreMat = Cal3D.CalLoader.loadCoreMaterialFromData(this.responseText);
				if(coreMat) {
					coreMat.setFilename(url);
					if(callback && callback.onload) {
						callback.onload(coreMat, url);
					}
				}
				else if(callback && callback.onerror) {
					callback.onerror(Cal3D.CalError.Code.INVALID_FILE_FORMAT, url);
				}
			}
		}
	};

	xhr.onerror = function() {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.FILE_NOT_FOUND, 'loader.js', 0, url);
		if(callback && callback.onerror) {
			callback.onerror(Cal3D.CalError.Code.FILE_NOT_FOUND, url);
		}
	};

	xhr.onprogress = function(evt) {
		if(callback && callback.onprogress) {
			callback.onprogress(evt.position / evt.totalSize, url);
		}
	};

	xhr.send();
};

/**
	Load a core material instance from a given data buffer.<br />
	@param {string} data The data buffer from which to load the core material instance.
	@returns {Cal3D.CalCoreMaterial} The core material instance loaded; null if any error happened.
*/
Cal3D.CalLoader.loadCoreMaterialFromData = function(data) {
	// check if this is a valid material file
	if(data.substring(0, 3) != Cal3D.CalLibraryConstants.MATERIAL_FILE_MAGIC) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null
	}

	var datasource = new Cal3D.CalBufferSource(data.substring(4, data.length));

	// check if the version is compatible with the library
	var version = datasource.readInteger();
	if( isNaN(version) || version < Cal3D.CalLibraryConstants.EARLIEST_COMPATIBLE_FILE_VERSION || 
		version > Cal3D.CalLibraryConstants.CURRENT_FILE_VERSION ) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INCOMPATIBLE_FILE_VERSION, 'loader.js');
		return null;
	}

	// allocate a new core material instance
	var coreMaterial = new Cal3D.CalCoreMaterial;

	var colorBuffer = [0, 0, 0, 0];

	// get the ambient color of the core material
	var ambientColor = new Cal3D.CalCoreMaterial.Color;
	datasource.readBytes(colorBuffer, colorBuffer.length);
	ambientColor.red   = colorBuffer[0];
	ambientColor.green = colorBuffer[1];
	ambientColor.blue  = colorBuffer[2];
	ambientColor.alpha = colorBuffer[3];

	// get the diffuse color of the core material
	var diffuseColor = new Cal3D.CalCoreMaterial.Color;
	datasource.readBytes(colorBuffer, colorBuffer.length);
	diffuseColor.red   = colorBuffer[0];
	diffuseColor.green = colorBuffer[1];
	diffuseColor.blue  = colorBuffer[2];
	diffuseColor.alpha = colorBuffer[3];

	// get the specular color of the core material
	var specularColor = new Cal3D.CalCoreMaterial.Color;
	datasource.readBytes(colorBuffer, colorBuffer.length);
	specularColor.red   = colorBuffer[0];
	specularColor.green = colorBuffer[1];
	specularColor.blue  = colorBuffer[2];
	specularColor.alpha = colorBuffer[3];

	// get the shininess factor of the core material
	var shininess = datasource.readFloat();

	// check if an error happened
	if(!datasource.ok()) {
		datasource.setError();
		return null;
	}

	// set the colors and the shininess
	coreMaterial.setAmbientColor(ambientColor);
	coreMaterial.setDiffuseColor(diffuseColor);
	coreMaterial.setSpecularColor(specularColor);
	coreMaterial.setShininess(shininess);

	// read the number of maps
	var mapCount = datasource.readInteger();
	if(isNaN(mapCount) || mapCount < 0) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	// reserve memory for all the material data
	if(!coreMaterial.reserve(mapCount)) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.MEMORY_ALLOCATION_FAILED, 'loader.js');
		return null;
	}

	// load all maps
	for(var mapId=0; mapId<mapCount; mapId++) {
		var map = new Cal3D.CalCoreMaterial.Map;

		// read the filename of the map
		map.filename = datasource.readString();

		// initialize the user data to null
		map.userData = null;

		// check if an error happened
		if(!datasource.ok()) {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			return null;
		}

		// set map in the core material instance
		coreMaterial.setMap(mapId, map);
	}

	return coreMaterial;
};

/**
	Load a core mesh instance from a given url.<br />
	@param {string} url The url where to load the core mesh instance.
	@param {object} callback An object which implements the loader callback interface to retrieve the core mesh instance loaded and/or the error information and/or the loading progress information.
*/
Cal3D.CalLoader.loadCoreMeshFromFile = function(url, callback) {
	if( url.length >= 3 && 
		url.substring(url.length - 3, url.length).toUpperCase() == Cal3D.CalLibraryConstants.MESH_XMLFILE_MAGIC ) {
		Cal3D.CalLoader.loadXmlCoreMesh(url, callback);
		return;
	}

	var xhr = new XMLHttpRequest;
	xhr.open('GET', url, true);
	xhr.overrideMimeType('text/plain; charset=x-user-defined');

	xhr.onreadystatechange = function() {
		if(this.readyState == 4) {
			if(this.status == 200 || this.status == 0) {
				var coreMesh = Cal3D.CalLoader.loadCoreMeshFromData(this.responseText);
				if(coreMesh) {
					coreMesh.setFilename(url);
					if(callback && callback.onload) {
						callback.onload(coreMesh, url);
					}
				}
				else if(callback && callback.onerror) {
					callback.onerror(Cal3D.CalError.Code.INVALID_FILE_FORMAT, url);
				}
			}
		}
	};

	xhr.onerror = function() {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.FILE_NOT_FOUND, 'loader.js', 0, url);
		if(callback && callback.onerror) {
			callback.onerror(Cal3D.CalError.Code.FILE_NOT_FOUND, url);
		}
	};

	xhr.onprogress = function(evt) {
		if(callback && callback.onprogress) {
			callback.onprogress(evt.position / evt.totalSize, url);
		}
	};

	xhr.send();
};

/**
	Load a core mesh instance from a given data buffer.<br />
	@param {string} data The data buffer from which to load the core mesh instance.
	@returns {Cal3D.CalCoreMesh} The core mesh instance loaded; null if any error happened.
*/
Cal3D.CalLoader.loadCoreMeshFromData = function(data) {
	// check if this is a valid mesh file
	if(data.substring(0, 3) != Cal3D.CalLibraryConstants.MESH_FILE_MAGIC) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null
	}

	var datasource = new Cal3D.CalBufferSource(data.substring(4, data.length));

	// check if the version is compatible with the library
	var version = datasource.readInteger();
	if( isNaN(version) || version < Cal3D.CalLibraryConstants.EARLIEST_COMPATIBLE_FILE_VERSION || 
		version > Cal3D.CalLibraryConstants.CURRENT_FILE_VERSION ) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INCOMPATIBLE_FILE_VERSION, 'loader.js');
		return null;
	}

	// get the number of submeshes
	var submeshCount = datasource.readInteger();
	if(isNaN(submeshCount)) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null
	}

	// create a new core mesh instance
	var coreMesh = new Cal3D.CalCoreMesh;

	// load all core submeshes
	for(var submeshId=0; submeshId<submeshCount; submeshId++) {
		// load the core submesh
		var coreSubmesh = Cal3D.CalLoader.loadCoreSubmesh(datasource);
		if(!coreSubmesh) {
			return null;
		}

		// add the core submesh to the core mesh instance
		coreMesh.addCoreSubmesh(coreSubmesh);
	}

	return coreMesh;
};

/**
	Load a core skeleton instance from a given url.<br />
	@param {string} url The url where to load the core skeleton instance.
	@param {object} callback An object which implements the loader callback interface to retrieve the core skeleton instance loaded and/or the error information and/or the loading progress information.
*/
Cal3D.CalLoader.loadCoreSkeletonFromFile = function(url, callback) {
	if( url.length >= 3 && 
		url.substring(url.length - 3, url.length).toUpperCase() == Cal3D.CalLibraryConstants.SKELETON_XMLFILE_MAGIC ) {
		Cal3D.CalLoader.loadXmlCoreSkeleton(url, callback);
		return;
	}

	var xhr = new XMLHttpRequest;
	xhr.open('GET', url, true);
	xhr.overrideMimeType('text/plain; charset=x-user-defined');

	xhr.onreadystatechange = function() {
		if(this.readyState == 4) {
			if(this.status == 200 || this.status == 0) {
				var coreSkeleton = Cal3D.CalLoader.loadCoreSkeletonFromData(this.responseText);
				if(coreSkeleton) {
					if(callback && callback.onload) {
						callback.onload(coreSkeleton, url);
					}
				}
				else if(callback && callback.onerror) {
					callback.onerror(Cal3D.CalError.Code.INVALID_FILE_FORMAT, url);
				}
			}
		}
	};

	xhr.onerror = function() {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.FILE_NOT_FOUND, 'loader.js', 0, url);
		if(callback && callback.onerror) {
			callback.onerror(Cal3D.CalError.Code.FILE_NOT_FOUND, url);
		}
	};

	xhr.onprogress = function(evt) {
		if(callback && callback.onprogress) {
			callback.onprogress(evt.position / evt.totalSize, url);
		}
	};

	xhr.send();
};

/**
	Load a core skeleton instance from a given data buffer.<br />
	@param {string} data The data buffer from which to load the core skeleton instance.
	@returns {Cal3D.CalCoreMesh} The core skeleton instance loaded; null if any error happened.
*/
Cal3D.CalLoader.loadCoreSkeletonFromData = function(data) {
	// check if this is a valid skeleton file
	if(data.substring(0, 3) != Cal3D.CalLibraryConstants.SKELETON_FILE_MAGIC) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		print('Error invalid file format!');
		return null
	}

	var datasource = new Cal3D.CalBufferSource(data.substring(4, data.length));
	print('Current offset: '+datasource.m_offset);
	// check if the version is compatible with the library
	var version = datasource.readInteger();
	print('version: '+version);
	if( isNaN(version) || version < Cal3D.CalLibraryConstants.EARLIEST_COMPATIBLE_FILE_VERSION || 
		version > Cal3D.CalLibraryConstants.CURRENT_FILE_VERSION ) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INCOMPATIBLE_FILE_VERSION, 'loader.js');
		print('Error invalid version!');
		return null;
	}

	// read the number of bones
	var boneCount = datasource.readInteger();
	print('NOF Bones: '+boneCount);
	if(isNaN(boneCount) || boneCount <= 0) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		print('Error no bones!');
		return null
	}
	
	// allocate a new core skeleton instance
	var coreSkeleton = new Cal3D.CalCoreSkeleton;
	
	// load all core bones
	for(var boneId=0; boneId<boneCount; boneId++) {
		// load the core bone
		print('load bone: '+boneId)
		var coreBone = Cal3D.CalLoader.loadCoreBones(datasource);
		if(!coreBone) {
			print('Not core bone');
			return null;
			
		}

		// set the core skeleton of the core bone instance
		coreBone.setCoreSkeleton(coreSkeleton);

		// add the core bone to the core skeleton instance
		coreSkeleton.addCoreBone(coreBone);

		// add a core skeleton mapping of the bone's name for quick reference later
		coreSkeleton.mapCoreBoneName(boneId, coreBone.getName());
	}
	print('loaded bones!');
	// calculate state of the core skeleton
	coreSkeleton.calculateState();
	print('calc state!');
	return coreSkeleton;
};

/**
	@private
*/
Cal3D.CalLoader.loadCoreBones = function(datasource) {
	if(!datasource.ok()) {
		datasource.setError();
		print('datasource Error!');
		return null;
	}

	// read the name of the bone
	var name = datasource.readString();
	print('bone: '+name);
	// get the translation of the bone
	var tx, ty, tz;
	tx = datasource.readFloat();
	ty = datasource.readFloat();
	tz = datasource.readFloat();
	print('x '+tx+' y '+ty+' z'+tz);
	// get the rotation of the bone
	var rx, ry, rz, rw;
	rx = datasource.readFloat();
	ry = datasource.readFloat();
	rz = datasource.readFloat();
	rw = datasource.readFloat();
	print('x '+rx+' y '+ry+' z '+rz+' w '+rw);
	// get the bone space translation of the bone
	var txBoneSpace, tyBoneSpace, tzBoneSpace;
	txBoneSpace = datasource.readFloat();
	tyBoneSpace = datasource.readFloat();
	tzBoneSpace = datasource.readFloat();
	print('x '+txBoneSpace+' y '+tyBoneSpace+' z'+tzBoneSpace);
	// get the bone space rotation of the bone
	var rxBoneSpace, ryBoneSpace, rzBoneSpace, rwBoneSpace;
	rxBoneSpace = datasource.readFloat();
	ryBoneSpace = datasource.readFloat();
	rzBoneSpace = datasource.readFloat();
	rwBoneSpace = datasource.readFloat();
	print('x '+rxBoneSpace+' y '+ryBoneSpace+' z '+rzBoneSpace+' w '+rwBoneSpace);
	// get the parent bone id
	var parentId = datasource.readInteger();
	print('Parent ID: '+parentId);
	var rot = new Cal3D.CalQuaternion(rx, ry, rz, rw);
	var rotbs = new Cal3D.CalQuaternion(rxBoneSpace, ryBoneSpace, rzBoneSpace, rwBoneSpace);
	var trans = new Cal3D.CalVector(tx, ty, tz);

	if (Cal3D.CalLoader.loadingMode & Cal3D.CalLoader.LOADER_ROTATE_X_AXIS) {
		if (parentId == -1) { // only root bone necessary
			var x_axis_90 = new Cal3D.CalQuaternion(0.7071067811, 0, 0, 0.7071067811);
			// root bone must have quaternion rotated
			rot.multQuaternionLocal(x_axis_90);
			// root bone must have translation rotated also
			trans.multQuaternionLocal(x_axis_90);
		}
	}


	// check if an error happened
	if(!datasource.ok()) {
		datasource.setError();
		print('Datasource problem!');
		return null;
	}

	// create a new core bone instance
	var coreBone = new Cal3D.CalCoreBone(name);

	// set the parent of the bone
	coreBone.setParentId(parentId);

	// set all attributes of the bone
	coreBone.setTranslation(trans);
	coreBone.setRotation(rot);
	coreBone.setTranslationBoneSpace(new Cal3D.CalVector(txBoneSpace, tyBoneSpace, tzBoneSpace));
	coreBone.setRotationBoneSpace(rotbs);

	// read the number of children
	var childCount = datasource.readInteger();
	print('Children: '+childCount);
	if(isNaN(childCount) || childCount < 0) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}
	
	// load all children ids
	for(; childCount>0; childCount--) {
		var childId = datasource.readInteger();
		print('child id '+childId);
		if(isNaN(childId) || childId < 0) {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			print('child id error! '+childId);
			return null;
		}

		coreBone.addChildId(childId);
	}

	return coreBone;
};

/**
	@private
*/
Cal3D.CalLoader.loadCoreKeyframe = function(datasource) {
	if(!datasource.ok()) {
		datasource.setError();
		return null;
	}

	// get the time of the keyframe
	var time = datasource.readFloat();

	// get the translation of the bone
	var tx, ty, tz;
	tx = datasource.readFloat();
	ty = datasource.readFloat();
	tz = datasource.readFloat();

	// get the rotation of the bone
	var rx, ry, rz, rw;
	rx = datasource.readFloat();
	ry = datasource.readFloat();
	rz = datasource.readFloat();
	rw = datasource.readFloat();

	// check if an error happened
	if(!datasource.ok()) {
		datasource.setError();
		return null;
	}

	// create a new core keyframe instance
	var coreKeyframe = new Cal3D.CalCoreKeyframe;
	if(!coreKeyframe.create())	{
		return null;
	}
	// set all attributes of the keyframe
	coreKeyframe.setTime(time);
	coreKeyframe.setTranslation(new Cal3D.CalVector(tx, ty, tz));
	coreKeyframe.setRotation(new Cal3D.CalQuaternion(rx, ry, rz, rw));

	return coreKeyframe;
};

/**
	@private
*/
Cal3D.CalLoader.loadCoreSubmesh = function(datasource) {
	if(!datasource.ok()) {
		datasource.setError();
		return null;
	}

	// get the material thread id of the submesh
	var coreMaterialThreadId = datasource.readInteger();

	// get the number of vertices
	var vertexCount = datasource.readInteger();

	// get the number of faces
	var faceCount = datasource.readInteger();

	// get the number of level-of-details
	var lodCount = datasource.readInteger();

	// get the number of springs
	var springCount = datasource.readInteger();

	// get the number of texture coordinates per vertex
	var textureCoordinateCount = datasource.readInteger();

	// check if an error happened
	if(!datasource.ok()) {
		datasource.setError();
		return null;
	}

	// allocate a new core submesh instance
	var coreSubmesh = new Cal3D.CalCoreSubmesh;

	// set the LOD step count
	coreSubmesh.setLodCount(lodCount);

	// set the core material id
	coreSubmesh.setCoreMaterialThreadId(coreMaterialThreadId);

	// reserve memory for all the submesh data
	if(!coreSubmesh.reserve(vertexCount, textureCoordinateCount, faceCount, springCount)) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.MEMORY_ALLOCATION_FAILED, 'loader.js');
		return null;
	}

	// load the tangent space enable flags.
	for(var textureCoordinateId=0; textureCoordinateId<textureCoordinateCount; textureCoordinateId++) {
		coreSubmesh.enableTangents(textureCoordinateId, false);
	}

	// load all vertices and their influences
	for(var vertexId=0; vertexId<vertexCount; vertexId++) {
		var vertex = new Cal3D.CalCoreSubmesh.Vertex;

		// load data of the vertex
		vertex.position.x = datasource.readFloat();
		vertex.position.y = datasource.readFloat();
		vertex.position.z = datasource.readFloat();
		vertex.normal.x = datasource.readFloat();
		vertex.normal.y = datasource.readFloat();
		vertex.normal.z = datasource.readFloat();
		vertex.collapseId = datasource.readInteger();
		vertex.faceCollapseCount = datasource.readInteger();

		// check if an error happened
		if(!datasource.ok()) {
			datasource.setError();
			return null;
		}

		// load all texture coordinates of the vertex
		for(var vartextureCoordinateId=0; textureCoordinateId<textureCoordinateCount; textureCoordinateId++) {
			var textureCoordinate = new Cal3D.CalCoreSubmesh.TextureCoordinate;

			// load the texture coordinate
			textureCoordinate.u = datasource.readFloat();
			textureCoordinate.v = datasource.readFloat();

			if(Cal3D.CalLoader.loadingMode & Cal3D.CalLoader.LOADER_INVERT_V_COORD) {
				textureCoordinate.v = 1 - textureCoordinate.v;
			}

			// check if an error happened
			if(!datasource.ok()) {
				datasource.setError();
				return null;
			}

			// set texture coordinate in the core submesh instance
			coreSubmesh.setTextureCoordinate(vertexId, textureCoordinateId, textureCoordinate);
		}

		// get the number of influences
		var influenceCount = datasource.readInteger();
		if(isNaN(influenceCount) || influenceCount < 0) {
			datasource.setError();
			return null;
		}

		// load all influences of the vertex
		for(var influenceId=0; influenceId<influenceCount; influenceId++) {
			// load data of the influence
			var influence = new Cal3D.CalCoreSubmesh.Influence;
			influence.boneId = datasource.readInteger();
			influence.weight = datasource.readFloat();
			vertex.vectorInfluence.push(influence);

			// check if an error happened
			if(!datasource.ok()) {
				datasource.setError();
				return null;
			}
		}

		// set vertex in the core submesh instance
		coreSubmesh.setVertex(vertexId, vertex);

		// load the physical property of the vertex if there are springs in the core submesh
		if(springCount > 0) {
			var physicalProperty = new Cal3D.CalCoreSubmesh.PhysicalProperty;

			// load data of the physical property
			physicalProperty.weight = datasource.readFloat();

			// check if an error happened
			if(!datasource.ok()) {
				datasource.setError();
				return null;
			}

			// set the physical property in the core submesh instance
			coreSubmesh.setPhysicalProperty(vertexId, physicalProperty);
		}
	}

	// load all springs
	for(var springId=0; springId<springCount; springId++) {
		var spring = new Cal3D.CalCoreSubmesh.Spring;

		// load data of the spring
		spring.vertexId[0] = datasource.readInteger();
		spring.vertexId[1] = datasource.readInteger();
		spring.springCoefficient = datasource.readFloat();
		spring.idleLength = datasource.readFloat();

		// check if an error happened
		if(!datasource.ok()) {
			datasource.setError();
			return null;
		}

		// set spring in the core submesh instance
		coreSubmesh.setSpring(springId, spring);
	}


	// load all faces
	var justOnce = 0;
	var flipModel = false;
	for(var faceId=0; faceId<faceCount; faceId++) {
		var face = new Cal3D.CalCoreSubmesh.Face;

		// load data of the face
		face.vertexId[0] = datasource.readInteger();
		face.vertexId[1] = datasource.readInteger();
		face.vertexId[2] = datasource.readInteger();

		// check if an error happened
		if(!datasource.ok()) {
			datasource.setError();
			return null;
		}

		// check if left-handed coord system is used by the object
		// can be done only once since the object has one system for all faces
		if(justOnce == 0) {
			// get vertexes of first face
			var vectorVertex = coreSubmesh.getVectorVertex();
			var v1 = vectorVertex[face.vertexId[0]];
			var v2 = vectorVertex[face.vertexId[1]];
			var v3 = vectorVertex[face.vertexId[2]];

			var point1 = new Cal3D.CalVector(v1.position);
			var point2 = new Cal3D.CalVector(v2.position);
			var point3 = new Cal3D.CalVector(v3.position);

			// gets vectors (v1-v2) and (v3-v2)
			var vect1 = Cal3D.vectorSub(point1, point2);
			var vect2 = Cal3D.vectorSub(point3, point2);

			// calculates normal of face
			var cross = Cal3D.vectorCross(vect1, vect2);
			var faceNormal = Cal3D.vectorScalarDiv(cross, cross.length());

			// compare the calculated normal with the normal of a vertex
			var maxNorm = new Cal3D.CalVector(v1.normal);

			// if the two vectors point to the same direction then the poly needs flipping
			// that is if the dot product > 0 it needs flipping
			if (Cal3D.vectorDot(faceNormal, maxNorm) > 0)
				flipModel = true;

			// flip the winding order if the loading flags request it
			if (Cal3D.CalLoader.loadingMode & Cal3D.CalLoader.LOADER_FLIP_WINDING)
				flipModel = !flipModel;

			justOnce = 1;
		}


		// flip if needed
		if(flipModel) {
			var tmp = face.vertexId[1];
			face.vertexId[1] = face.vertexId[2];
			face.vertexId[2] = tmp;
		}

		// set face in the core submesh instance
		coreSubmesh.setFace(faceId, face);
	}

	return coreSubmesh;
};

/**
	@private
*/
Cal3D.CalLoader.loadCoreTrack = function(datasource, skel, duration) {
	if(!datasource.ok()) {
		datasource.setError();
		return null;
	}

	// read the bone id
	var coreBoneId = datasource.readInteger();
	if(isNaN(coreBoneId) || coreBoneId < 0) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	// create a new core track instance
	var coreTrack = new Cal3D.CalCoreTrack;
	if(!coreTrack.create()) {
		return null;
	}

	// link the core track to the appropriate core bone instance
	coreTrack.setCoreBoneId(coreBoneId);

	// read the number of keyframes
	var keyframeCount = datasource.readInteger();
	if(isNaN(keyframeCount) || keyframeCount <= 0) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	// load all core keyframes
	for(var keyframeId=0; keyframeId<keyframeCount; keyframeId++) {
		// load the core keyframe
		var coreKeyframe = Cal3D.CalLoader.loadCoreKeyframe(datasource);
		if(!coreKeyframe) {
			coreTrack.destroy();
			return null;
		}

		if(Cal3D.CalLoader.loadingMode & Cal3D.CalLoader.LOADER_ROTATE_X_AXIS) {
			// check for anim rotation
			if (skel && skel.getCoreBone(coreBoneId).getParentId() == -1) {  // root bone
				var x_axis_90 = new Cal3D.CalQuaternion(0.7071067811, 0, 0, 0.7071067811);

				// rotate root bone quaternion
				var rot = new Cal3D.CalQuaternion(coreKeyframe.getRotation());
				rot.multQuaternionLocal(x_axis_90);
				coreKeyframe.setRotation(rot);

				// rotate root bone displacement
				var trans = new Cal3D.CalVector(coreKeyframe.getTranslation());
				trans.multQuaternionLocal(x_axis_90);
				coreKeyframe.setTranslation(trans);
			}
		}    

		// add the core keyframe to the core track instance
		coreTrack.addCoreKeyframe(coreKeyframe);
	}

	return coreTrack;
};

/**
	@private
*/
Cal3D.CalLoader.loadXmlCoreAnimation = function(url, skel, callback) {
	var xhr = new XMLHttpRequest;
	xhr.open('GET', url, true);
	xhr.overrideMimeType('text/xml');

	xhr.onreadystatechange = function() {
		if(this.readyState == 4) {
			if(this.status == 200 || this.status == 0) {
				var coreAnim = Cal3D.CalLoader.parseXmlCoreAnimation(this.responseXML, skel);
				if(coreAnim) {
					coreAnim.setFilename(url);
					if(callback && callback.onload) {
						callback.onload(coreAnim, url);
					}
				}
				else if(callback && callback.onerror) {
					callback.onerror(Cal3D.CalError.Code.INVALID_FILE_FORMAT, url);
				}
			}
		}
	};

	xhr.onerror = function() {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.FILE_NOT_FOUND, 'loader.js', 0, url);
		if(callback && callback.onerror) {
			callback.onerror(Cal3D.CalError.Code.FILE_NOT_FOUND, url);
		}
	};

	xhr.onprogress = function(evt) {
		if(callback && callback.onprogress) {
			callback.onprogress(evt.position / evt.totalSize, url);
		}
	};

	xhr.send();
};

/**
	@private
*/
Cal3D.CalLoader.loadXmlCoreSkeleton = function(url, callback) {
	var xhr = new XMLHttpRequest;
	xhr.open('GET', url, true);
	xhr.overrideMimeType('text/xml');

	xhr.onreadystatechange = function() {
		if(this.readyState == 4) {
			if(this.status == 200 || this.status == 0) {
				var coreSkeleton = Cal3D.CalLoader.parseXmlCoreSkeleton(this.responseXML);
				if(coreSkeleton) {
					if(callback && callback.onload) {
						callback.onload(coreSkeleton, url);
					}
				}
				else if(callback && callback.onerror) {
					callback.onerror(Cal3D.CalError.Code.INVALID_FILE_FORMAT, url);
				}
			}
		}
	};

	xhr.onerror = function() {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.FILE_NOT_FOUND, 'loader.js', 0, url);
		if(callback && callback.onerror) {
			callback.onerror(Cal3D.CalError.Code.FILE_NOT_FOUND, url);
		}
	};

	xhr.onprogress = function(evt) {
		if(callback && callback.onprogress) {
			callback.onprogress(evt.position / evt.totalSize, url);
		}
	};

	xhr.send();
};

/**
	@private
*/
Cal3D.CalLoader.loadXmlCoreMesh = function(url, callback) {
	var xhr = new XMLHttpRequest;
	xhr.open('GET', url, true);
	xhr.overrideMimeType('text/xml');

	xhr.onreadystatechange = function() {
		if(this.readyState == 4) {
			if(this.status == 200 || this.status == 0) {
				var coreMesh = Cal3D.CalLoader.parseXmlCoreMesh(this.responseXML);
				if(coreMesh) {
					coreMesh.setFilename(url);
					if(callback && callback.onload) {
						callback.onload(coreMesh, url);
					}
				}
				else if(callback && callback.onerror) {
					callback.onerror(Cal3D.CalError.Code.INVALID_FILE_FORMAT, url);
				}
			}
		}
	};

	xhr.onerror = function() {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.FILE_NOT_FOUND, 'loader.js', 0, url);
		if(callback && callback.onerror) {
			callback.onerror(Cal3D.CalError.Code.FILE_NOT_FOUND, url);
		}
	};

	xhr.onprogress = function(evt) {
		if(callback && callback.onprogress) {
			callback.onprogress(evt.position / evt.totalSize, url);
		}
	};

	xhr.send();
};

/**
	@private
*/
Cal3D.CalLoader.loadXmlCoreMaterial = function(url, callback) {
	var xhr = new XMLHttpRequest;
	xhr.open('GET', url, true);
	xhr.overrideMimeType('text/xml');

	xhr.onreadystatechange = function() {
		if(this.readyState == 4) {
			if(this.status == 200 || this.status == 0) {
				var coreMat = Cal3D.CalLoader.parseXmlCoreMaterial(this.responseXML);
				if(coreMat) {
					coreMat.setFilename(url);
					if(callback && callback.onload) {
						callback.onload(coreMat, url);
					}
				}
				else if(callback && callback.onerror) {
					callback.onerror(Cal3D.CalError.Code.INVALID_FILE_FORMAT, url);
				}
			}
		}
	};

	xhr.onerror = function() {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.FILE_NOT_FOUND, 'loader.js', 0, url);
		if(callback && callback.onerror) {
			callback.onerror(Cal3D.CalError.Code.FILE_NOT_FOUND, url);
		}
	};

	xhr.onprogress = function(evt) {
		if(callback && callback.onprogress) {
			callback.onprogress(evt.position / evt.totalSize, url);
		}
	};

	xhr.send();
};

/**
	@private
*/
Cal3D.CalLoader.parseConfigFile = function(text) {
	var linePattern = /^\s*(\w+)\s*=\s*([^:*?"<>|\r\n]+)\s*$/;

	var ret = {
		path: '', 
		scale: 1, 
		skeletons: [], 
		animations: [], 
		meshes: [], 
		materials: []
	};

	var lines = text.split('\n');
	for(var i=0; i<lines.length; i++) {
		var r = linePattern.exec(lines[i]);
		if(!r)
			continue;

		var key   = r[1];
		var value = r[2];

		switch(key)
		{
		case 'path':
			if(ret.path == '') {
				ret.path = value;
				var endChar = ret.path.charAt(ret.path.length - 1);
				if(endChar != '/' && endChar != '\\')
					ret.path += '/';
			}
			break;
		case 'scale':
			ret.scale = parseFloat(value);
			break;
		case 'skeleton':
			ret.skeletons.push(value);
			break;
		case 'animation':
			ret.animations.push(value);
			break;
		case 'mesh':
			ret.meshes.push(value);
			break;
		case 'material':
			ret.materials.push(value);
			break;
		default:
			break;
		}
	}

	return ret;
};

/**
	@private
*/
Cal3D.CalLoader.parseXmlCoreAnimation = function(xmldoc, skel) {
	if(!xmldoc) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		print('invalid animation file');
		return null;
		
	}

	var animationElem = xmldoc.firstChild;
	if(!animationElem || animationElem.tagName != 'ANIMATION') {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		print('no animation header');
		return null;
	}

	if( animationElem.hasAttribute('MAGIC') && 
		animationElem.getAttribute('MAGIC') != Cal3D.CalLibraryConstants.ANIMATION_XMLFILE_MAGIC ) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		print('no magic');
		return null;
	}

	if( animationElem.hasAttribute('VERSION') && 
		parseInt(animationElem.getAttribute('VERSION') < Cal3D.CalLibraryConstants.EARLIEST_COMPATIBLE_FILE_VERSION)) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INCOMPATIBLE_FILE_VERSION, 'loader.js');
		print('Wrong anim version');
		return null;
	}

	var duration = parseFloat(animationElem.getAttribute('DURATION'));
	var trackCount = parseInt(animationElem.getAttribute('NUMTRACKS'));

	// create a new core animation instance
	var coreAnimation = new Cal3D.CalCoreAnimation;

	// check for a valid duration
	if(duration <= 0) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_ANIMATION_DURATION, 'loader.js');
		print('invalid duration');
		return null;
	}

	// set the duration in the core animation instance
	coreAnimation.setDuration(duration);

	var trackElem = animationElem.firstElementChild;
	
	// load all core tracks
	for(var trackId=0; trackId<trackCount; trackId++) {
		if(!trackElem || trackElem.tagName != 'TRACK') {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			print('invalid tracks');
			return null;
		}

		// create the core track instance
		var coreTrack = new Cal3D.CalCoreTrack;
		if(!coreTrack.create()) {
			print('could not create track');
			return null;
		}

		var coreBoneId = parseInt(trackElem.getAttribute('BONEID'));

		
		// link the core track to the appropriate core bone instance
		coreTrack.setCoreBoneId(coreBoneId);

		// read the number of keyframes
		var keyframeCount = parseInt(trackElem.getAttribute('NUMKEYFRAMES'));

		if(keyframeCount <= 0) {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			print('invalid keyframes');
			return null;
		}

		var keyframeElem = trackElem.firstElementChild;

		// load all core keyframes
		for(var keyframeId=0; keyframeId<keyframeCount; keyframeId++) {
			// load the core keyframe
			if(!keyframeElem || keyframeElem.tagName != 'KEYFRAME') {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				print('not keyframe tag');
				return null;
			}

			var time = parseFloat(keyframeElem.getAttribute('TIME'));

			var translationElem = keyframeElem.firstElementChild;
			var rotationElem;

			if(!translationElem || translationElem.tagName != 'TRANSLATION') {
				print('no translation info: tag - '+translationElem.tagName);
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				return null;
			}

			var r = Cal3D.CalLoader.pattern_float3.exec(translationElem.textContent);
			if(!r) {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				return null;
			}

			var tx, ty, tz;
			tx = parseFloat(r[1]);
			ty = parseFloat(r[2]);
			tz = parseFloat(r[3]);

			rotationElem = translationElem.nextElementSibling;

			if(!rotationElem || rotationElem.tagName != 'ROTATION') {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				print('no rotation info');
				return null;
			}

			r = Cal3D.CalLoader.pattern_float4.exec(rotationElem.textContent);
			if(!r) {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				return null;
			}

			var rx, ry, rz, rw;
			rx = parseFloat(r[1]);
			ry = parseFloat(r[2]);
			rz = parseFloat(r[3]);
			rw = parseFloat(r[4]);

			// create a new core keyframe instance
			var coreKeyframe = new Cal3D.CalCoreKeyframe;
			if(!coreKeyframe.create()) {
				return null;			  
			}
			// set all attributes of the keyframe
			coreKeyframe.setTime(time);

			coreKeyframe.setTranslation(new Cal3D.CalVector(tx, ty, tz));

			coreKeyframe.setRotation(new Cal3D.CalQuaternion(rx, ry, rz, rw));

			if(Cal3D.CalLoader.loadingMode & Cal3D.CalLoader.LOADER_ROTATE_X_AXIS) {
				// check for anim rotation
				if(skel && skel.getCoreBone(coreBoneId).getParentId() == -1) {	// root bone
					// rotate root bone quaternion
					var x_axis_90 = new Cal3D.CalQuaternion(0.7071067811, 0, 0, 0.7071067811);

					var rot = new Cal3D.CalQuaternion(coreKeyframe.getRotation());
					rot.multQuaternionLocal(x_axis_90);
					coreKeyframe.setRotation(rot);

					// rotate root bone displacement
					var trans = new Cal3D.CalVector(coreKeyframe.getTranslation());
					trans.multQuaternionLocal(x_axis_90);
					coreKeyframe.setTranslation(trans);
				}
			}    

			// add the core keyframe to the core track instance
			coreTrack.addCoreKeyframe(coreKeyframe);

			// go to next keyframe
			keyframeElem = keyframeElem.nextElementSibling;
		}

		coreAnimation.addCoreTrack(coreTrack);

		// go to next track
		trackElem = trackElem.nextElementSibling;
	}

	return coreAnimation;
};
Cal3D.CalLoader.StringtoXml = function(data) {

  var parser=new DOMParser();
  var xmlDoc=parser.parseFromString(data,"text/xml");
  return xmlDoc;
}
/**
	@private
*/
Cal3D.CalLoader.parseXmlCoreSkeleton = function(xmldoc) {
	if(!xmldoc) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		print('invalid file format');
		return null;
	}
	print('checking doc');
	var skeletonElem = xmldoc.firstChild;
	if(!skeletonElem || skeletonElem.tagName != 'SKELETON') {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		print('wrong tag: '+skeletonElem.tagName);
		return null;
	}
	print('has skeleton');
	if( skeletonElem.hasAttribute('MAGIC') && 
		skeletonElem.getAttribute('MAGIC') != Cal3D.CalLibraryConstants.SKELETON_XMLFILE_MAGIC ) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	if( skeletonElem.hasAttribute('VERSION') && 
		parseInt(skeletonElem.getAttribute('VERSION')) < Cal3D.CalLibraryConstants.EARLIEST_COMPATIBLE_FILE_VERSION ) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INCOMPATIBLE_FILE_VERSION, 'loader.js');
		return null;
	}

	// create a new core skeleton instance
	var coreSkeleton = new Cal3D.CalCoreSkeleton();

	var boneCount = parseInt(skeletonElem.getAttribute('NUMBONES'));

	var boneElem = skeletonElem.firstElementChild;
	for(; boneElem; boneElem=boneElem.nextElementSibling) {
		if(boneElem.tagName != 'BONE') {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			return null;
		}    

		var name = boneElem.getAttribute('NAME');

		// get the translation of the bone

		var translationElem = boneElem.firstElementChild;
		if(!translationElem || translationElem.tagName != 'TRANSLATION') {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			return null;
		}

		var r = Cal3D.CalLoader.pattern_float3.exec(translationElem.textContent);
		if(!r) {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			return null;
		}

		var tx, ty, tz;
		tx = parseFloat(r[1]);
		ty = parseFloat(r[2]);
		tz = parseFloat(r[3]);

		// get the rotation of the bone

		var rotationElem = translationElem.nextElementSibling;
		if(!rotationElem || rotationElem.tagName != 'ROTATION') {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			return null;
		}

		r = Cal3D.CalLoader.pattern_float4.exec(rotationElem.textContent);
		if(!r) {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			return null;
		}

		var rx, ry, rz, rw;
		rx = parseFloat(r[1]);
		ry = parseFloat(r[2]);
		rz = parseFloat(r[3]);
		rw = parseFloat(r[4]);

		// get the bone space translation of the bone

		var translationBoneSpaceElem = rotationElem.nextElementSibling;
		if(!translationBoneSpaceElem || translationBoneSpaceElem.tagName != 'LOCALTRANSLATION') {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			return null;
		}

		r = Cal3D.CalLoader.pattern_float3.exec(translationBoneSpaceElem.textContent);
		if(!r) {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			return null;
		}

		var txBoneSpace, tyBoneSpace, tzBoneSpace;
		txBoneSpace = parseFloat(r[1]);
		tyBoneSpace = parseFloat(r[2]);
		tzBoneSpace = parseFloat(r[3]);

		// get the bone space rotation of the bone

		var rotationBoneSpaceElem = translationBoneSpaceElem.nextElementSibling;
		if(!rotationBoneSpaceElem || rotationBoneSpaceElem.tagName != 'LOCALROTATION') {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			return null;
		}

		r = Cal3D.CalLoader.pattern_float4.exec(rotationBoneSpaceElem.textContent);
		if(!r) {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			return null;
		}

		var rxBoneSpace, ryBoneSpace, rzBoneSpace, rwBoneSpace;
		rxBoneSpace = parseFloat(r[1]);
		ryBoneSpace = parseFloat(r[2]);
		rzBoneSpace = parseFloat(r[3]);
		rwBoneSpace = parseFloat(r[4]);

		// get the parent bone id

		var parentElem = rotationBoneSpaceElem.nextElementSibling;
		if(!parentElem || parentElem.tagName != 'PARENTID') {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			return null;
		}

		var parentId = parseInt(parentElem.textContent);

		// create a new core bone instance
		var coreBone = new Cal3D.CalCoreBone(name);

		// set parent of the bone
		coreBone.setParentId(parentId);

		// set all attributes of the bone

		var trans = new Cal3D.CalVector(tx, ty, tz);
		var rot = new Cal3D.CalQuaternion(rx, ry, rz, rw);

		if (Cal3D.CalLoader.loadingMode & Cal3D.CalLoader.LOADER_ROTATE_X_AXIS) {
			if(parentId == -1) {	// only root bone necessary
				var x_axis_90 = new Cal3D.CalQuaternion(0.7071067811, 0, 0, 0.7071067811);
				// root bone must have quaternion rotated
				rot.multQuaternionLocal(x_axis_90);
				// Root bone must have translation rotated also
				trans.multQuaternionLocal(x_axis_90);
			}
		}    

		coreBone.setTranslation(trans);
		coreBone.setRotation(rot);
		coreBone.setTranslationBoneSpace(new Cal3D.CalVector(txBoneSpace, tyBoneSpace, tzBoneSpace));
		coreBone.setRotationBoneSpace(new Cal3D.CalQuaternion(rxBoneSpace, ryBoneSpace, rzBoneSpace, rwBoneSpace));

		var childElem = parentElem.nextElementSibling;
		for(; childElem; childElem=childElem.nextElementSibling) {
			if(childElem.tagName != 'CHILDID') {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				return null;
			}

			var childId = parseInt(childElem.textContent);
			coreBone.addChildId(childId);
		}

		// set the core skeleton of the core bone instance
		coreBone.setCoreSkeleton(coreSkeleton);

		// add the core bone to the core skeleton instance
		coreSkeleton.addCoreBone(coreBone);

	}

	coreSkeleton.calculateState();

	return coreSkeleton;
};

/**
	@private
*/
Cal3D.CalLoader.parseXmlCoreMesh = function(xmldoc) {
	if(!xmldoc) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
		print('Invalid File Format');
	}
	print('Loading Mesh');
	var meshElem = xmldoc.firstChild;
	if(!meshElem || meshElem.tagName != 'MESH') {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		print('Invalid Tag: '+meshElem.tagName);
		return null;
	}

	if( meshElem.hasAttribute('MAGIC') && 
		meshElem.getAttribute('MAGIC') != Cal3D.CalLibraryConstants.MESH_XMLFILE_MAGIC ) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		print('Has no magic');
		return null;
	}

	if( meshElem.hasAttribute('VERSION') && 
		parseInt(meshElem.getAttribute('VERSION')) < Cal3D.CalLibraryConstants.EARLIEST_COMPATIBLE_FILE_VERSION ) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INCOMPATIBLE_FILE_VERSION, 'loader.js');
		print('Wrong version');
		return null;
	}

	// get the number of submeshes
	var submeshCount = parseInt(meshElem.getAttribute('NUMSUBMESH'));
	print('Submeshes: '+submeshCount);
	// create a new core mesh instance
	var coreMesh = new Cal3D.CalCoreMesh;

	var submeshElem = meshElem.firstElementChild;

	// load all core submeshes
	for(var submeshId=0; submeshId<submeshCount; submeshId++) {
		if(!submeshElem || submeshElem.tagName != 'SUBMESH') {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			print('Cant find submeshes');
			return null;
		}

		// get the material thread id of the submeshElem
		var coreMaterialThreadId = parseInt(submeshElem.getAttribute('MATERIAL'));

		// get the number of vertices
		var vertexCount = parseInt(submeshElem.getAttribute('NUMVERTICES'));

		// get the number of faces
		var faceCount = parseInt(submeshElem.getAttribute('NUMFACES'));

		// get the number of level-of-details
		var lodCount = parseInt(submeshElem.getAttribute('NUMLODSTEPS'));

		// get the number of springs
		var springCount = parseInt(submeshElem.getAttribute('NUMSPRINGS'));

		// get the number of texture coordinates
		var textureCoordinateCount = parseInt(submeshElem.getAttribute('NUMTEXCOORDS'));

		// create a new core submesh instance
		var coreSubmesh = new Cal3D.CalCoreSubmesh();

		// set the LOD step count
		coreSubmesh.setLodCount(lodCount);

		// set the core material id
		coreSubmesh.setCoreMaterialThreadId(coreMaterialThreadId);

		// reserve space for all the submesh data
		if(!coreSubmesh.reserve(vertexCount, textureCoordinateCount, faceCount, springCount)) {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.MEMORY_ALLOCATION_FAILED, 'loader.js');
			return null;
		}

		var vertexElem = submeshElem.firstElementChild;
		
		// load all vertices and their influences
		for(var vertexId=0; vertexId<vertexCount; vertexId++) {
			if(!vertexElem || vertexElem.tagName != 'VERTEX') {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				print('No vertex tag');
				return null;
			}      

			var vertex = new Cal3D.CalCoreSubmesh.Vertex;

			var posElem = vertexElem.firstElementChild;
			if(!posElem || posElem.tagName != 'POS') {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				print('No POS elem');
				return null;
				
			}

			var r = Cal3D.CalLoader.pattern_float3.exec(posElem.textContent);
			if(!r) {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				print('Invalid POS data');
				return null;
			}

			vertex.position.x = parseFloat(r[1]);
			vertex.position.y = parseFloat(r[2]);
			vertex.position.z = parseFloat(r[3]);

			var normElem = posElem.nextElementSibling;
			if(!normElem || normElem.tagName != 'NORM') {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				print('No Norm Elem');
				return null;
			}

			r = Cal3D.CalLoader.pattern_float3.exec(normElem.textContent);
			if(!r) {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				return null;
			}

			vertex.normal.x = parseFloat(r[1]);
			vertex.normal.y = parseFloat(r[2]);
			vertex.normal.z = parseFloat(r[3]);

			var collapseElem = normElem.nextElementSibling;
			if(collapseElem && collapseElem.tagName == 'COLLAPSEID') {
				vertex.collapseId = parseInt(collapseElem.textContent);

				var collapseCountElem = collapseElem.nextElementSibling;
				if(!collapseCountElem || collapseCountElem.tagName != 'COLLAPSECOUNT') {
					Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
					print('no COllapse elem');
					return null;
				}

				vertex.faceCollapseCount = parseInt(collapseCountElem.textContent);

				collapseElem = collapseCountElem.nextElementSibling;
			}
			else {
				vertex.collapseId = -1;
				vertex.faceCollapseCount = 0;
			}


			var texcoordElem = collapseElem;
			if(texcoordElem.tagName=='COLOR')
			{
				texcoordElem = texcoordElem.nextElementSibling;
			}
			//print('got texcoord elem');
			// load all texture coordinates of the vertex
			for(var textureCoordinateId=0; textureCoordinateId<textureCoordinateCount; textureCoordinateId++) {
				if(!texcoordElem || texcoordElem.tagName != 'TEXCOORD') {
					Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
					print('No Texcoords');
					return null;
				}

				// load texture coordinate
				var textureCoordinate = new Cal3D.CalCoreSubmesh.TextureCoordinate;

				var r = Cal3D.CalLoader.pattern_float2.exec(texcoordElem.textContent);
				if(!r) {
					Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
					print('Bad Tex coords');
					return null;
				}

				textureCoordinate.u = parseFloat(r[1]);
				textureCoordinate.v = parseFloat(r[2]);

				if(Cal3D.CalLoader.loadingMode & Cal3D.CalLoader.LOADER_INVERT_V_COORD) {
					textureCoordinate.v = 1 - textureCoordinate.v;
				}

				// set texture coordinate into the core submesh instance
				coreSubmesh.setTextureCoordinate(vertexId, textureCoordinateId, textureCoordinate);

				// go to next texture coordinate
				texcoordElem = texcoordElem.nextElementSibling;
			}

			// get the number of influences
			var influenceCount = parseInt(vertexElem.getAttribute('NUMINFLUENCES'));
			if(influenceCount < 0) {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				print('No influences');
				return null;
			}

			var influenceElem = texcoordElem;

			// load all influences of the vertex
			for(var influenceId=0; influenceId<influenceCount; influenceId++) {
				if(!influenceElem || influenceElem.tagName != 'INFLUENCE') {
					Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
					print('Invalid Influence');
					return null;
				}

				var influence = new Cal3D.CalCoreSubmesh.Influence;
				influence.boneId = parseInt(influenceElem.getAttribute('ID'));
				influence.weight = parseFloat(influenceElem.textContent);
				vertex.vectorInfluence.push(influence);

				// go to next influence
				influenceElem = influenceElem.nextElementSibling;
			}

			// set vertex in the core submesh instance
			coreSubmesh.setVertex(vertexId, vertex);
			//print('Set vertices');
			var physiqueElem = influenceElem;

			// load the physical property of the vertex if there are springs in the core submesh
			if(springCount > 0) {
				if(!physiqueElem || physiqueElem.tagName != 'PHYSIQUE') {
					Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
					print('No Physique Tag');
					return null;
				}

				var physicalProperty = new Cal3D.CalCoreSubmesh.PhysicalProperty;
				physicalProperty.weight = parseFloat(physiqueElem.textContent);

				// set the physical property in the core submesh instance
				coreSubmesh.setPhysicalProperty(vertexId, physicalProperty);          
			}

			// go to next vertex
			vertexElem = vertexElem.nextElementSibling;
		}

		var springElem = vertexElem;

		// load all springs
		for(var springId=0; springId<springCount; springId++) {
			if(!springElem || springElem.tagName != 'SPRING') {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				print('No Spring Tag');
				return null;
			}

			var spring = new Cal3D.CalCoreSubmesh.Spring;

			var r = Cal3D.CalLoader.pattern_int2.exec(springElem.getAttribute('VERTEXID'));
			if(!r) {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				print('No Spring Vertex');
				return null;
			}

			spring.vertexId[0] = parseInt(r[1]);
			spring.vertexId[1] = parseInt(r[2]);
			spring.springCoefficient = parseFloat(springElem.getAttribute('COEF'));
			spring.idleLength = parseFloat(springElem.getAttribute('LENGTH'));

			// set spring in the core submesh instance
			coreSubmesh.setSpring(springId, spring);

			// go to next spring
			springElem = springElem.nextElementSibling;
		}

		var faceElem = springElem;

		// load all faces
		for(var faceId=0; faceId<faceCount; faceId++) {
			if(!faceElem || faceElem.tagName != 'FACE') {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				print('No Face Tag @'+faceId);
				return null;
			}

			var face = new Cal3D.CalCoreSubmesh.Face;

			// load data of the face
			var r = Cal3D.CalLoader.pattern_int3.exec(faceElem.getAttribute('VERTEXID'));
			if(!r) {
				Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
				print('Unable to load face data');
				return null;
			}

			face.vertexId[0] = parseInt(r[1]);
			face.vertexId[1] = parseInt(r[2]);
			face.vertexId[2] = parseInt(r[3]);

			coreSubmesh.setFace(faceId, face);

			// go to next face
			faceElem = faceElem.nextElementSibling;
		}

		// add the core submesh to the core mesh instance
		coreMesh.addCoreSubmesh(coreSubmesh);
		print('Add submesh');
		// go to next submesh
		submeshElem = submeshElem.nextElementSibling;
	}
	
	return coreMesh;
};

/**
	@private
*/
Cal3D.CalLoader.parseXmlCoreMaterial = function(xmldoc) {
	if(!xmldoc) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	var materialElem = xmldoc.firstChild;
	if(!materialElem || materialElem.tagName != 'MATERIAL') {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	if( materialElem.hasAttribute('MAGIC') && 
		materialElem.getAttribute('MAGIC') != Cal3D.CalLibraryConstants.MATERIAL_XMLFILE_MAGIC ) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	if( materialElem.hasAttribute('VERSION') &&
		parseInt(materialElem.getAttribute('VERSION')) < Cal3D.CalLibraryConstants.EARLIEST_COMPATIBLE_FILE_VERSION ) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INCOMPATIBLE_FILE_VERSION, 'loader.js');
		return null;
	}

	// create a new core material instance
	var coreMaterial = new Cal3D.CalCoreMaterial;

	// read ambient color

	var ambientElem = materialElem.firstElementChild;
	if(!ambientElem || ambientElem.tagName != 'AMBIENT') {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	var ambientColor = new Cal3D.CalCoreMaterial.Color;

	var r = Cal3D.CalLoader.pattern_int4.exec(ambientElem.textContent);
	if(!r) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	ambientColor.red   = parseInt(r[1]);
	ambientColor.green = parseInt(r[2]);
	ambientColor.blue  = parseInt(r[3]);
	ambientColor.alpha = parseInt(r[4]);

	// read diffuse color

	var diffuseElem = ambientElem.nextElementSibling;
	if(!diffuseElem || diffuseElem.tagName != 'DIFFUSE') {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	var diffuseColor = new Cal3D.CalCoreMaterial.Color;
	
	r = Cal3D.CalLoader.pattern_int4.exec(diffuseElem.textContent);
	if(!r) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	diffuseColor.red   = parseInt(r[1]);
	diffuseColor.green = parseInt(r[2]);
	diffuseColor.blue  = parseInt(r[3]);
	diffuseColor.alpha = parseInt(r[4]);

	// read specular color

	var specularElem = diffuseElem.nextElementSibling;
	if(!specularElem || specularElem.tagName != 'SPECULAR') {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	var specularColor = new Cal3D.CalCoreMaterial.Color;

	r = Cal3D.CalLoader.pattern_int4.exec(specularElem.textContent);
	if(!r) {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	specularColor.red   = parseInt(r[1]);
	specularColor.green = parseInt(r[2]);
	specularColor.blue  = parseInt(r[3]);
	specularColor.alpha = parseInt(r[4]);

	// read shininess 

	var shininessElem = specularElem.nextElementSibling;
	if(!shininessElem || shininessElem.tagName != 'SHININESS') {
		Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
		return null;
	}

	var shininess = parseFloat(shininessElem.textContent);

	// set the colors and the shininess
	coreMaterial.setAmbientColor(ambientColor);
	coreMaterial.setDiffuseColor(diffuseColor);
	coreMaterial.setSpecularColor(specularColor);
	coreMaterial.setShininess(shininess);

	var matFilenames = [];

	var mapElem = shininessElem.nextElementSibling;
	for(; mapElem; mapElem=mapElem.nextElementSibling) {
		if(!mapElem || mapElem.tagName != 'MAP') {
			Cal3D.CalError.setLastError(Cal3D.CalError.Code.INVALID_FILE_FORMAT, 'loader.js');
			return null;
		}

		matFilenames.push(mapElem.textContent);
	}

	coreMaterial.reserve(matFilenames.length);

	for(var mapId=0; mapId<matFilenames.length; mapId++) {
		var map = new Cal3D.CalCoreMaterial.Map;

		map.filename = matFilenames[mapId];    
		map.userData = null;

		// set map in the core material instance
		coreMaterial.setMap(mapId, map);
	}

	return coreMaterial;
};

/**
	@private
*/
Cal3D.CalLoader.loadingMode = 0;

/**
	@private
*/
Cal3D.CalLoader.pattern_int2 = /([-+]?\b\d+\b)\s+([-+]?\b\d+\b)/;

/**
	@private
*/
Cal3D.CalLoader.pattern_int3 = /([-+]?\b\d+\b)\s+([-+]?\b\d+\b)\s+([-+]?\b\d+\b)/;

/**
	@private
*/
Cal3D.CalLoader.pattern_int4 = /([-+]?\b\d+\b)\s+([-+]?\b\d+\b)\s+([-+]?\b\d+\b)\s+([-+]?\b\d+\b)/;

/**
	@private
*/
Cal3D.CalLoader.pattern_float2 = /([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)/;

/**
	@private
*/
Cal3D.CalLoader.pattern_float3 = /([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)/;

/**
	@private
*/
Cal3D.CalLoader.pattern_float4 = /([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)\s+([-+]?\b(?:[0-9]*\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\b)/;

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@class CalBufferSource
*/
Cal3D.CalBufferSource = function(inputBuffer) {
	this.m_buffer = inputBuffer;
	this.m_offset = 0;
};

/**
	Check if the buffer source is in good state.<br />
	@returns {boolean} true if the buffer source is in good state; false if not.
*/
Cal3D.CalBufferSource.prototype.ok = function() {
	return this.m_buffer != null;
};

/**
	Set the error code and message related to a the buffer source.
*/
Cal3D.CalBufferSource.prototype.setError = function() {
	Cal3D.CalError.setLastError(Cal3D.CalError.Code.NULL_BUFFER, 'datasource.js');
};

/**
	Read a given number of bytes from the buffer source.<br />
	@param {Array} The array where the bytes are stored into.
	@param {number} count The number of bytes to be read.
	@returns {number} The number of bytes that was actually read.
*/
Cal3D.CalBufferSource.prototype.readBytes = function(buffer, count) {
	if(!this.ok() || !buffer || count <= 0)
		return 0;

	if(this.m_offset + count > this.m_buffer.length)
		count = this.m_buffer.length - this.m_offset;

	for(var i=0; i<count; i++) {
		buffer[i] = this.m_buffer[this.m_offset + i].charCodeAt(0) & 0xff;
	}
	this.m_offset += count;

	return count;
};

/**
	Read a 32-bits little endian float number from the buffer source.<br />
	@returns {number} The float value. NaN will be returned if any error happened.
*/
Cal3D.CalBufferSource.prototype.readFloatb = function() {
	if(!this.ok())
		return NaN;
	if(this.m_offset + 4 > this.m_buffer.length)
		return NaN;
		
	this.m_offset += 4;
	return parseFloat(this.m_buffer.substring(this.m_offset-4, 4));
}

Cal3D.CalBufferSource.prototype.readFloat = function() {
	if(!this.ok())
		return NaN;

	if(this.m_offset + 4 > this.m_buffer.length)
		return NaN;

	var mLen = 23;
	var eLen = 8;		// 4 * 8 - 23 - 1
	var eMax = 255;		// (1 << eLen) - 1;
	var eBias = 127;	// eMax >> 1;

	var i = 3; 
	var d = -1; 
	var s = this.m_buffer[this.m_offset + i].charCodeAt(0) & 0xff; 
	i += d; 
	var bits = -7;
	var e = s & ((1 << (-bits)) - 1);
	s >>= -bits;
	bits += eLen
	while(bits > 0) {
		e = e * 256 + (this.m_buffer[this.m_offset + i].charCodeAt(0) & 0xff);
		i += d;
		bits -= 8;
	}

	var m = e & ((1 << (-bits)) - 1);
	e >>= -bits;
	bits += mLen;
	while(bits > 0) {
		 m = m * 256 + (this.m_buffer[this.m_offset + i].charCodeAt(0) & 0xff);
		 i += d;
		 bits -= 8;
	}

	this.m_offset += 4;
	print('new offset: '+this.m_offset);
	switch(e) {
		case 0:		// 0 or denormalized number
			e = 1 - eBias;
			break;
		case eMax:	// NaN or +/-Infinity
			return m ? NaN : ((s ? -1 : 1) * Infinity);
		default:	// normalized number
			m = m + Math.pow(2, mLen);
			e = e - eBias;
			break;
	}

	return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
};

/**
	Read a 32-bits little endian signed integer number from the buffer source.<br />
	@returns {number} The integer value. NaN will be returned if any error happened.
*/
Cal3D.CalBufferSource.prototype.readInteger = function() {
	if(!this.ok())
		return NaN;

	if(this.m_offset + 4 > this.m_buffer.length)
		return NaN;

	var rv = 0, f = 1;
	for(var i=0; i<4; i++) {
		rv += ((this.m_buffer[this.m_offset + i].charCodeAt(0) & 0xff) * f);
		f *= 256;
	}

	if(rv & 0x80000000)
		rv -= 0x100000000;

	this.m_offset += 4;

	return rv;
};

/**
	Read a string from the buffer source.<br />
	@returns {string} The string value. An empty string will be returned if any error happened.
*/
Cal3D.CalBufferSource.prototype.readString = function() {
	if(!this.ok())
		return '';

	// read and skip the 'string length' 32-bits integer field
	var length = this.readInteger();
	print('name len: '+length);
	if(isNaN(length) || length <= 0)
		return '';

	// read out all characters other than the null-terminator('\0')
	var s = this.m_buffer.substring(this.m_offset, this.m_offset + length-1);
	this.m_offset += length;

	return s;
};

/*****************************************************************************
* Cal3D 3d character animation library
* Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*****************************************************************************/

/*****************************************************************************
* Cal3DJS Javascript port of Cal3D for WebGL and canvas applications
* by Humu humu2009@gmail.com
* http://code.google.com/p/cal3djs/
*****************************************************************************/


/**
	@namespace CalError
*/
Cal3D.CalError = {};

/**
	The error code enumerations.
*/
Cal3D.CalError.Code = {
	OK:							0, 
	INTERNAL:					1, 
	INVALID_HANDLE:				2, 
	MEMORY_ALLOCATION_FAILED:	3, 
	FILE_NOT_FOUND:				4, 
	INVALID_FILE_FORMAT:		5, 
	FILE_PARSER_FAILED:			6, 
	INDEX_BUILD_FAILED:			7, 
	NO_PARSER_DOCUMENT:			8, 
	INVALID_ANIMATION_DURATION:	9, 
	BONE_NOT_FOUND:				10, 
	INVALID_ATTRIBUTE_VALUE:	11, 
	INVALID_KEYFRAME_COUNT:		12, 
	INVALID_ANIMATION_TYPE:		13, 
	FILE_CREATION_FAILED:		14, 
	FILE_WRITING_FAILED:		15, 
	INCOMPATIBLE_FILE_VERSION:	16, 
	NO_MESH_IN_MODEL:			17, 
	BAD_DATA_SOURCE:			18, 
	NULL_BUFFER:				19, 
	INVALID_MIXER_TYPE:			20, 
	MAX_ERROR_CODE:				21
};

/**
	Get the code of the last error.<br />
	@returns {number} The code of the last error.
*/
Cal3D.CalError.getLastErrorCode = function() {
	return Cal3D.CalError.m_lastErrorCode;
};

/**
	Get the name of the source file where the last error occured.<br />
	@returns {string} The name of the file where the last error occured.
*/
Cal3D.CalError.getLastErrorFile = function() {
	return Cal3D.CalError.m_lastErrorFile;
};

/**
	Get the line number in the source file where the last error occured.<br />
	@returns {number} The line number where the last error occured.
*/
Cal3D.CalError.getLastErrorLine = function() {
	return Cal3D.CalError.m_lastErrorLine;
};

/**
	Get the supplementary description of the last error.<br />
	@returns {string} The supplementary description of the last error.
*/
Cal3D.CalError.getLastErrorText = function() {
	return Cal3D.CalError.m_lastErrorText;
};

/**
	Dump all information about the last error to the standard output.<br />
*/
Cal3D.CalError.printLastError = function() {
	throw 'not implemented error';
};

/**
	Set all the information about the last error.<br />
	@param {number} code The code of the last error.
	@param {string} filename The name of the source file where the last error occured.
	@param {number} line The line number in the source file where the last error occured.
	@param {string} text The supplementary description of the last error.
*/
Cal3D.CalError.setLastError = function(code, filename, line, text) {
	if(code >= Cal3D.CalError.Code.MAX_ERROR_CODE)
		code = Cal3D.CalError.Code.INTERNAL;

	Cal3D.CalError.m_lastErrorCode = code;
	Cal3D.CalError.m_lastErrorFile = (filename != undefined) ? filename : '';
	Cal3D.CalError.m_lastErrorLine = (line != undefined) ? line : -1;
	Cal3D.CalError.m_lastErrorText = (text != undefined) ? text: '';
};

/**
	Get description of a given error.<br />
	@param {number} code The error code.
	@returns {string} The description of the given error code.
*/
Cal3D.CalError.getErrorDescription = function(code) {
	switch(code)
	{
	case Cal3D.CalError.Code.OK:                         return 'No error found';
	case Cal3D.CalError.Code.INTERNAL:                   return 'Internal error';
	case Cal3D.CalError.Code.INVALID_HANDLE:             return 'Invalid handle as argument';
	case Cal3D.CalError.Code.MEMORY_ALLOCATION_FAILED:   return 'Memory allocation failed';
	case Cal3D.CalError.Code.FILE_NOT_FOUND:             return 'File not found';
	case Cal3D.CalError.Code.INVALID_FILE_FORMAT:        return 'Invalid file format';
	case Cal3D.CalError.Code.FILE_PARSER_FAILED:         return 'Parser failed to process file';
	case Cal3D.CalError.Code.INDEX_BUILD_FAILED:         return 'Building of the index failed';
	case Cal3D.CalError.Code.NO_PARSER_DOCUMENT:         return 'There is no document to parse';
	case Cal3D.CalError.Code.INVALID_ANIMATION_DURATION: return 'The duration of the animation is invalid';
	case Cal3D.CalError.Code.BONE_NOT_FOUND:             return 'Bone not found';
	case Cal3D.CalError.Code.INVALID_ATTRIBUTE_VALUE:    return 'Invalid attribute value';
	case Cal3D.CalError.Code.INVALID_KEYFRAME_COUNT:     return 'Invalid number of keyframes';
	case Cal3D.CalError.Code.INVALID_ANIMATION_TYPE:     return 'Invalid animation type';
	case Cal3D.CalError.Code.FILE_CREATION_FAILED:       return 'Failed to create file';
	case Cal3D.CalError.Code.FILE_WRITING_FAILED:        return 'Failed to write to file';
	case Cal3D.CalError.Code.INCOMPATIBLE_FILE_VERSION:  return 'Incompatible file version';
	case Cal3D.CalError.Code.NO_MESH_IN_MODEL:           return 'No mesh attached to the model';
	case Cal3D.CalError.Code.BAD_DATA_SOURCE:            return 'Cannot read from data source';
	case Cal3D.CalError.Code.NULL_BUFFER:                return 'Memory buffer is null';
	case Cal3D.CalError.Code.INVALID_MIXER_TYPE:         return 'The CalModel mixer is not a CalMixer instance';
	default:											 return 'Unknown error';
	}
};

/**
	Get description of the last error.<br />
	@returns {string} The description of the last error.
*/
Cal3D.CalError.getLastErrorDescription = function() {
	return Cal3D.CalError.getErrorDescription(Cal3D.CalError.getLastErrorCode());
};

Cal3D.CalError.m_lastErrorCode = Cal3D.CalError.Code.OK;
Cal3D.CalError.m_lastErrorFile = '';
Cal3D.CalError.m_lastErrorLine = -1;
Cal3D.CalError.m_lastErrorText = '';

function c_App(){
	Object.call(this);
}
c_App.m_new=function(){
	if((bb_app__app)!=null){
		error("App has already been created");
	}
	bb_app__app=this;
	bb_app__delegate=c_GameDelegate.m_new.call(new c_GameDelegate);
	bb_app__game.SetDelegate(bb_app__delegate);
	return this;
}
c_App.prototype.p_OnResize=function(){
	return 0;
}
c_App.prototype.p_OnCreate=function(){
	return 0;
}
c_App.prototype.p_OnSuspend=function(){
	return 0;
}
c_App.prototype.p_OnResume=function(){
	return 0;
}
c_App.prototype.p_OnUpdate=function(){
	return 0;
}
c_App.prototype.p_OnLoading=function(){
	return 0;
}
c_App.prototype.p_OnRender=function(){
	return 0;
}
c_App.prototype.p_OnClose=function(){
	bb_app_EndApp();
	return 0;
}
c_App.prototype.p_OnBack=function(){
	this.p_OnClose();
	return 0;
}
function c_Game(){
	c_App.call(this);
	this.m_init_gl=false;
	this.m_dt=null;
	this.m_cam=null;
	this.m_light=null;
	this.m_TestModel=null;
	this.m_txt=null;
	this.m_touchBegin=0;
	this.m_oldTouchX=0;
	this.m_oldTouchY=0;
	this.m_lr=.0;
	this.m_ud=.0;
}
c_Game.prototype=extend_class(c_App);
c_Game.m_new=function(){
	c_App.m_new.call(this);
	return this;
}
c_Game.prototype.p_OnCreate=function(){
	bb_opengles20_SetRender(0);
	bb_app_SetUpdateRate(30);
	return 0;
}
c_Game.prototype.p_Init=function(){
	if(this.m_init_gl==true){
		return 0;
	}
	this.m_dt=c_DeltaTimer.m_new.call(new c_DeltaTimer,30.0);
	if(!((c_TPixmap.m_PreLoadPixmap(["mojo_font.png"]))!=0)){
		return 0;
	}
	this.m_cam=bb_functions_CreateCamera(null);
	this.m_cam.p_CameraClsColor(0.0,0.0,80.0);
	this.m_cam.p_PositionEntity(0.0,4.0,-10.0,0);
	this.m_light=bb_functions_CreateLight(1,null);
	this.m_light.p_PositionEntity(0.0,3.0,-3.0,0);
	this.m_TestModel=c_Cal3dB3d.m_new.call(new c_Cal3dB3d);
	this.m_TestModel.p_SetSkeleton("monkey://data/paladin/test.xsf");
	this.m_TestModel.p_AddMesh("monkey://data/paladin/testp.xmf");
	this.m_TestModel.p_AddAnim("monkey://data/paladin/run.xaf");
	this.m_TestModel.p_Build();
	if(this.m_TestModel.m_CModel.getMixer().blendCycle(0,1.0,0.0)){
		print("Execute Run!");
	}
	this.m_txt=c_TText.m_CreateText2D(null,"","",96,9,0);
	print("main: init done");
	this.m_init_gl=true;
	return 0;
}
c_Game.prototype.p_OnUpdate=function(){
	if(((bb_input_KeyHit(432))!=0) || ((bb_input_KeyHit(27))!=0)){
		error("");
	}
	if(this.m_init_gl==true){
	}
	if(this.m_init_gl==false){
		this.p_Init();
		return 0;
	}
	this.m_dt.p_UpdateDelta();
	var t_cr=(bb_input_KeyDown(37)-bb_input_KeyDown(39));
	var t_cu=(bb_input_KeyDown(40)-bb_input_KeyDown(38));
	var t_camin=(bb_input_KeyDown(87)-bb_input_KeyDown(83));
	var t_camup=(bb_input_KeyDown(68)-bb_input_KeyDown(65));
	var t_turnzx=(bb_input_KeyDown(90)-bb_input_KeyDown(88));
	if(((bb_input_TouchDown(0))!=0) && !((bb_input_TouchDown(1))!=0)){
		if(!((this.m_touchBegin)!=0)){
			this.m_oldTouchX=((bb_input_TouchX(0))|0);
			this.m_oldTouchY=((bb_input_TouchY(0))|0);
			this.m_touchBegin=1;
		}
		this.m_lr=(bb_input_TouchX(0)-(this.m_oldTouchX))*0.5;
		this.m_ud=(-bb_input_TouchY(0)+(this.m_oldTouchY))*0.5;
		this.m_oldTouchX=((bb_input_TouchX(0))|0);
		this.m_oldTouchY=((bb_input_TouchY(0))|0);
	}else{
		if((bb_input_TouchDown(1))!=0){
			if(!((this.m_touchBegin)!=0)){
				this.m_oldTouchX=((bb_input_TouchX(0))|0);
				this.m_oldTouchY=((bb_input_TouchY(0))|0);
				this.m_touchBegin=1;
			}
			t_camup=(-bb_input_TouchX(0)+(this.m_oldTouchX))*0.1;
			t_camin=(-bb_input_TouchY(0)+(this.m_oldTouchY))*0.1;
			this.m_oldTouchX=((bb_input_TouchX(0))|0);
			this.m_oldTouchY=((bb_input_TouchY(0))|0);
		}else{
			this.m_touchBegin=0;
		}
	}
	this.m_cam.p_MoveEntity(t_camup,0.0,t_camin);
	this.m_cam.p_TurnEntity(t_cu,t_cr,0.0,0);
	this.m_txt.p_SetText("Cam: "+String(t_camup)+" "+String(t_camin)+" "+String(t_cu)+" "+String(t_cr)+" delta: "+String(this.m_dt.m_delta),0.0,0.0,0.0,0);
	this.m_txt.p_HideEntity();
	this.m_txt.p_Draw(0.0,0.0,false);
	this.m_TestModel.p_Update5(this.m_dt.m_delta/30.0);
	bb_functions_UpdateWorld(1.0);
	return 0;
}
c_Game.prototype.p_OnRender=function(){
	bb_functions_RenderWorld();
	return 0;
}
var bb_app__app=null;
function c_GameDelegate(){
	BBGameDelegate.call(this);
	this.m__graphics=null;
	this.m__audio=null;
	this.m__input=null;
}
c_GameDelegate.prototype=extend_class(BBGameDelegate);
c_GameDelegate.m_new=function(){
	return this;
}
c_GameDelegate.prototype.StartGame=function(){
	this.m__graphics=(new gxtkGraphics);
	bb_graphics_SetGraphicsDevice(this.m__graphics);
	bb_graphics_SetFont(null,32);
	this.m__audio=(new gxtkAudio);
	bb_audio_SetAudioDevice(this.m__audio);
	this.m__input=c_InputDevice.m_new.call(new c_InputDevice);
	bb_input_SetInputDevice(this.m__input);
	bb_app_ValidateDeviceWindow(false);
	bb_app_EnumDisplayModes();
	bb_app__app.p_OnCreate();
}
c_GameDelegate.prototype.SuspendGame=function(){
	bb_app__app.p_OnSuspend();
	this.m__audio.Suspend();
}
c_GameDelegate.prototype.ResumeGame=function(){
	this.m__audio.Resume();
	bb_app__app.p_OnResume();
}
c_GameDelegate.prototype.UpdateGame=function(){
	bb_app_ValidateDeviceWindow(true);
	this.m__input.p_BeginUpdate();
	bb_app__app.p_OnUpdate();
	this.m__input.p_EndUpdate();
}
c_GameDelegate.prototype.RenderGame=function(){
	bb_app_ValidateDeviceWindow(true);
	var t_mode=this.m__graphics.BeginRender();
	if((t_mode)!=0){
		bb_graphics_BeginRender();
	}
	if(t_mode==2){
		bb_app__app.p_OnLoading();
	}else{
		bb_app__app.p_OnRender();
	}
	if((t_mode)!=0){
		bb_graphics_EndRender();
	}
	this.m__graphics.EndRender();
}
c_GameDelegate.prototype.KeyEvent=function(t_event,t_data){
	this.m__input.p_KeyEvent(t_event,t_data);
	if(t_event!=1){
		return;
	}
	var t_1=t_data;
	if(t_1==432){
		bb_app__app.p_OnClose();
	}else{
		if(t_1==416){
			bb_app__app.p_OnBack();
		}
	}
}
c_GameDelegate.prototype.MouseEvent=function(t_event,t_data,t_x,t_y){
	this.m__input.p_MouseEvent(t_event,t_data,t_x,t_y);
}
c_GameDelegate.prototype.TouchEvent=function(t_event,t_data,t_x,t_y){
	this.m__input.p_TouchEvent(t_event,t_data,t_x,t_y);
}
c_GameDelegate.prototype.MotionEvent=function(t_event,t_data,t_x,t_y,t_z){
	this.m__input.p_MotionEvent(t_event,t_data,t_x,t_y,t_z);
}
c_GameDelegate.prototype.DiscardGraphics=function(){
	this.m__graphics.DiscardGraphics();
}
var bb_app__delegate=null;
var bb_app__game=null;
function bbMain(){
	c_Game.m_new.call(new c_Game);
	return 0;
}
var bb_graphics_device=null;
function bb_graphics_SetGraphicsDevice(t_dev){
	bb_graphics_device=t_dev;
	return 0;
}
function c_Image(){
	Object.call(this);
	this.m_surface=null;
	this.m_width=0;
	this.m_height=0;
	this.m_frames=[];
	this.m_flags=0;
	this.m_tx=.0;
	this.m_ty=.0;
	this.m_source=null;
}
c_Image.m_DefaultFlags=0;
c_Image.m_new=function(){
	return this;
}
c_Image.prototype.p_SetHandle=function(t_tx,t_ty){
	this.m_tx=t_tx;
	this.m_ty=t_ty;
	this.m_flags=this.m_flags&-2;
	return 0;
}
c_Image.prototype.p_ApplyFlags=function(t_iflags){
	this.m_flags=t_iflags;
	if((this.m_flags&2)!=0){
		var t_=this.m_frames;
		var t_2=0;
		while(t_2<t_.length){
			var t_f=t_[t_2];
			t_2=t_2+1;
			t_f.m_x+=1;
		}
		this.m_width-=2;
	}
	if((this.m_flags&4)!=0){
		var t_3=this.m_frames;
		var t_4=0;
		while(t_4<t_3.length){
			var t_f2=t_3[t_4];
			t_4=t_4+1;
			t_f2.m_y+=1;
		}
		this.m_height-=2;
	}
	if((this.m_flags&1)!=0){
		this.p_SetHandle((this.m_width)/2.0,(this.m_height)/2.0);
	}
	if(this.m_frames.length==1 && this.m_frames[0].m_x==0 && this.m_frames[0].m_y==0 && this.m_width==this.m_surface.Width() && this.m_height==this.m_surface.Height()){
		this.m_flags|=65536;
	}
	return 0;
}
c_Image.prototype.p_Init2=function(t_surf,t_nframes,t_iflags){
	this.m_surface=t_surf;
	this.m_width=((this.m_surface.Width()/t_nframes)|0);
	this.m_height=this.m_surface.Height();
	this.m_frames=new_object_array(t_nframes);
	for(var t_i=0;t_i<t_nframes;t_i=t_i+1){
		this.m_frames[t_i]=c_Frame.m_new.call(new c_Frame,t_i*this.m_width,0);
	}
	this.p_ApplyFlags(t_iflags);
	return this;
}
c_Image.prototype.p_Init3=function(t_surf,t_x,t_y,t_iwidth,t_iheight,t_nframes,t_iflags,t_src,t_srcx,t_srcy,t_srcw,t_srch){
	this.m_surface=t_surf;
	this.m_source=t_src;
	this.m_width=t_iwidth;
	this.m_height=t_iheight;
	this.m_frames=new_object_array(t_nframes);
	var t_ix=t_x;
	var t_iy=t_y;
	for(var t_i=0;t_i<t_nframes;t_i=t_i+1){
		if(t_ix+this.m_width>t_srcw){
			t_ix=0;
			t_iy+=this.m_height;
		}
		if(t_ix+this.m_width>t_srcw || t_iy+this.m_height>t_srch){
			error("Image frame outside surface");
		}
		this.m_frames[t_i]=c_Frame.m_new.call(new c_Frame,t_ix+t_srcx,t_iy+t_srcy);
		t_ix+=this.m_width;
	}
	this.p_ApplyFlags(t_iflags);
	return this;
}
c_Image.prototype.p_Width=function(){
	return this.m_width;
}
function c_GraphicsContext(){
	Object.call(this);
	this.m_defaultFont=null;
	this.m_font=null;
	this.m_firstChar=0;
	this.m_matrixSp=0;
	this.m_ix=1.0;
	this.m_iy=.0;
	this.m_jx=.0;
	this.m_jy=1.0;
	this.m_tx=.0;
	this.m_ty=.0;
	this.m_tformed=0;
	this.m_matDirty=0;
	this.m_color_r=.0;
	this.m_color_g=.0;
	this.m_color_b=.0;
	this.m_alpha=.0;
	this.m_blend=0;
	this.m_scissor_x=.0;
	this.m_scissor_y=.0;
	this.m_scissor_width=.0;
	this.m_scissor_height=.0;
}
c_GraphicsContext.m_new=function(){
	return this;
}
var bb_graphics_context=null;
function bb_data_FixDataPath(t_path){
	var t_i=t_path.indexOf(":/",0);
	if(t_i!=-1 && t_path.indexOf("/",0)==t_i+1){
		return t_path;
	}
	if(string_startswith(t_path,"./") || string_startswith(t_path,"/")){
		return t_path;
	}
	return "monkey://data/"+t_path;
}
function c_Frame(){
	Object.call(this);
	this.m_x=0;
	this.m_y=0;
}
c_Frame.m_new=function(t_x,t_y){
	this.m_x=t_x;
	this.m_y=t_y;
	return this;
}
c_Frame.m_new2=function(){
	return this;
}
function bb_graphics_LoadImage(t_path,t_frameCount,t_flags){
	var t_surf=bb_graphics_device.LoadSurface(bb_data_FixDataPath(t_path));
	if((t_surf)!=null){
		return (c_Image.m_new.call(new c_Image)).p_Init2(t_surf,t_frameCount,t_flags);
	}
	return null;
}
function bb_graphics_LoadImage2(t_path,t_frameWidth,t_frameHeight,t_frameCount,t_flags){
	var t_surf=bb_graphics_device.LoadSurface(bb_data_FixDataPath(t_path));
	if((t_surf)!=null){
		return (c_Image.m_new.call(new c_Image)).p_Init3(t_surf,0,0,t_frameWidth,t_frameHeight,t_frameCount,t_flags,null,0,0,t_surf.Width(),t_surf.Height());
	}
	return null;
}
function bb_graphics_SetFont(t_font,t_firstChar){
	if(!((t_font)!=null)){
		if(!((bb_graphics_context.m_defaultFont)!=null)){
			bb_graphics_context.m_defaultFont=bb_graphics_LoadImage("mojo_font.png",96,2);
		}
		t_font=bb_graphics_context.m_defaultFont;
		t_firstChar=32;
	}
	bb_graphics_context.m_font=t_font;
	bb_graphics_context.m_firstChar=t_firstChar;
	return 0;
}
var bb_audio_device=null;
function bb_audio_SetAudioDevice(t_dev){
	bb_audio_device=t_dev;
	return 0;
}
function c_InputDevice(){
	Object.call(this);
	this.m__joyStates=new_object_array(4);
	this.m__keyDown=new_bool_array(512);
	this.m__keyHitPut=0;
	this.m__keyHitQueue=new_number_array(33);
	this.m__keyHit=new_number_array(512);
	this.m__charGet=0;
	this.m__charPut=0;
	this.m__charQueue=new_number_array(32);
	this.m__mouseX=.0;
	this.m__mouseY=.0;
	this.m__touchX=new_number_array(32);
	this.m__touchY=new_number_array(32);
	this.m__accelX=.0;
	this.m__accelY=.0;
	this.m__accelZ=.0;
}
c_InputDevice.m_new=function(){
	for(var t_i=0;t_i<4;t_i=t_i+1){
		this.m__joyStates[t_i]=c_JoyState.m_new.call(new c_JoyState);
	}
	return this;
}
c_InputDevice.prototype.p_PutKeyHit=function(t_key){
	if(this.m__keyHitPut==this.m__keyHitQueue.length){
		return;
	}
	this.m__keyHit[t_key]+=1;
	this.m__keyHitQueue[this.m__keyHitPut]=t_key;
	this.m__keyHitPut+=1;
}
c_InputDevice.prototype.p_BeginUpdate=function(){
	for(var t_i=0;t_i<4;t_i=t_i+1){
		var t_state=this.m__joyStates[t_i];
		if(!BBGame.Game().PollJoystick(t_i,t_state.m_joyx,t_state.m_joyy,t_state.m_joyz,t_state.m_buttons)){
			break;
		}
		for(var t_j=0;t_j<32;t_j=t_j+1){
			var t_key=256+t_i*32+t_j;
			if(t_state.m_buttons[t_j]){
				if(!this.m__keyDown[t_key]){
					this.m__keyDown[t_key]=true;
					this.p_PutKeyHit(t_key);
				}
			}else{
				this.m__keyDown[t_key]=false;
			}
		}
	}
}
c_InputDevice.prototype.p_EndUpdate=function(){
	for(var t_i=0;t_i<this.m__keyHitPut;t_i=t_i+1){
		this.m__keyHit[this.m__keyHitQueue[t_i]]=0;
	}
	this.m__keyHitPut=0;
	this.m__charGet=0;
	this.m__charPut=0;
}
c_InputDevice.prototype.p_KeyEvent=function(t_event,t_data){
	var t_1=t_event;
	if(t_1==1){
		if(!this.m__keyDown[t_data]){
			this.m__keyDown[t_data]=true;
			this.p_PutKeyHit(t_data);
			if(t_data==1){
				this.m__keyDown[384]=true;
				this.p_PutKeyHit(384);
			}else{
				if(t_data==384){
					this.m__keyDown[1]=true;
					this.p_PutKeyHit(1);
				}
			}
		}
	}else{
		if(t_1==2){
			if(this.m__keyDown[t_data]){
				this.m__keyDown[t_data]=false;
				if(t_data==1){
					this.m__keyDown[384]=false;
				}else{
					if(t_data==384){
						this.m__keyDown[1]=false;
					}
				}
			}
		}else{
			if(t_1==3){
				if(this.m__charPut<this.m__charQueue.length){
					this.m__charQueue[this.m__charPut]=t_data;
					this.m__charPut+=1;
				}
			}
		}
	}
}
c_InputDevice.prototype.p_MouseEvent=function(t_event,t_data,t_x,t_y){
	var t_2=t_event;
	if(t_2==4){
		this.p_KeyEvent(1,1+t_data);
	}else{
		if(t_2==5){
			this.p_KeyEvent(2,1+t_data);
			return;
		}else{
			if(t_2==6){
			}else{
				return;
			}
		}
	}
	this.m__mouseX=t_x;
	this.m__mouseY=t_y;
	this.m__touchX[0]=t_x;
	this.m__touchY[0]=t_y;
}
c_InputDevice.prototype.p_TouchEvent=function(t_event,t_data,t_x,t_y){
	var t_3=t_event;
	if(t_3==7){
		this.p_KeyEvent(1,384+t_data);
	}else{
		if(t_3==8){
			this.p_KeyEvent(2,384+t_data);
			return;
		}else{
			if(t_3==9){
			}else{
				return;
			}
		}
	}
	this.m__touchX[t_data]=t_x;
	this.m__touchY[t_data]=t_y;
	if(t_data==0){
		this.m__mouseX=t_x;
		this.m__mouseY=t_y;
	}
}
c_InputDevice.prototype.p_MotionEvent=function(t_event,t_data,t_x,t_y,t_z){
	var t_4=t_event;
	if(t_4==10){
	}else{
		return;
	}
	this.m__accelX=t_x;
	this.m__accelY=t_y;
	this.m__accelZ=t_z;
}
c_InputDevice.prototype.p_KeyHit=function(t_key){
	if(t_key>0 && t_key<512){
		return this.m__keyHit[t_key];
	}
	return 0;
}
c_InputDevice.prototype.p_KeyDown=function(t_key){
	if(t_key>0 && t_key<512){
		return this.m__keyDown[t_key];
	}
	return false;
}
c_InputDevice.prototype.p_TouchX=function(t_index){
	if(t_index>=0 && t_index<32){
		return this.m__touchX[t_index];
	}
	return 0.0;
}
c_InputDevice.prototype.p_TouchY=function(t_index){
	if(t_index>=0 && t_index<32){
		return this.m__touchY[t_index];
	}
	return 0.0;
}
function c_JoyState(){
	Object.call(this);
	this.m_joyx=new_number_array(2);
	this.m_joyy=new_number_array(2);
	this.m_joyz=new_number_array(2);
	this.m_buttons=new_bool_array(32);
}
c_JoyState.m_new=function(){
	return this;
}
var bb_input_device=null;
function bb_input_SetInputDevice(t_dev){
	bb_input_device=t_dev;
	return 0;
}
var bb_app__devWidth=0;
var bb_app__devHeight=0;
function bb_app_ValidateDeviceWindow(t_notifyApp){
	var t_w=bb_app__game.GetDeviceWidth();
	var t_h=bb_app__game.GetDeviceHeight();
	if(t_w==bb_app__devWidth && t_h==bb_app__devHeight){
		return;
	}
	bb_app__devWidth=t_w;
	bb_app__devHeight=t_h;
	if(t_notifyApp){
		bb_app__app.p_OnResize();
	}
}
function c_DisplayMode(){
	Object.call(this);
	this.m__width=0;
	this.m__height=0;
}
c_DisplayMode.m_new=function(t_width,t_height){
	this.m__width=t_width;
	this.m__height=t_height;
	return this;
}
c_DisplayMode.m_new2=function(){
	return this;
}
function c_Map(){
	Object.call(this);
	this.m_root=null;
}
c_Map.m_new=function(){
	return this;
}
c_Map.prototype.p_Compare=function(t_lhs,t_rhs){
}
c_Map.prototype.p_FindNode=function(t_key){
	var t_node=this.m_root;
	while((t_node)!=null){
		var t_cmp=this.p_Compare(t_key,t_node.m_key);
		if(t_cmp>0){
			t_node=t_node.m_right;
		}else{
			if(t_cmp<0){
				t_node=t_node.m_left;
			}else{
				return t_node;
			}
		}
	}
	return t_node;
}
c_Map.prototype.p_Contains=function(t_key){
	return this.p_FindNode(t_key)!=null;
}
c_Map.prototype.p_RotateLeft=function(t_node){
	var t_child=t_node.m_right;
	t_node.m_right=t_child.m_left;
	if((t_child.m_left)!=null){
		t_child.m_left.m_parent=t_node;
	}
	t_child.m_parent=t_node.m_parent;
	if((t_node.m_parent)!=null){
		if(t_node==t_node.m_parent.m_left){
			t_node.m_parent.m_left=t_child;
		}else{
			t_node.m_parent.m_right=t_child;
		}
	}else{
		this.m_root=t_child;
	}
	t_child.m_left=t_node;
	t_node.m_parent=t_child;
	return 0;
}
c_Map.prototype.p_RotateRight=function(t_node){
	var t_child=t_node.m_left;
	t_node.m_left=t_child.m_right;
	if((t_child.m_right)!=null){
		t_child.m_right.m_parent=t_node;
	}
	t_child.m_parent=t_node.m_parent;
	if((t_node.m_parent)!=null){
		if(t_node==t_node.m_parent.m_right){
			t_node.m_parent.m_right=t_child;
		}else{
			t_node.m_parent.m_left=t_child;
		}
	}else{
		this.m_root=t_child;
	}
	t_child.m_right=t_node;
	t_node.m_parent=t_child;
	return 0;
}
c_Map.prototype.p_InsertFixup=function(t_node){
	while(((t_node.m_parent)!=null) && t_node.m_parent.m_color==-1 && ((t_node.m_parent.m_parent)!=null)){
		if(t_node.m_parent==t_node.m_parent.m_parent.m_left){
			var t_uncle=t_node.m_parent.m_parent.m_right;
			if(((t_uncle)!=null) && t_uncle.m_color==-1){
				t_node.m_parent.m_color=1;
				t_uncle.m_color=1;
				t_uncle.m_parent.m_color=-1;
				t_node=t_uncle.m_parent;
			}else{
				if(t_node==t_node.m_parent.m_right){
					t_node=t_node.m_parent;
					this.p_RotateLeft(t_node);
				}
				t_node.m_parent.m_color=1;
				t_node.m_parent.m_parent.m_color=-1;
				this.p_RotateRight(t_node.m_parent.m_parent);
			}
		}else{
			var t_uncle2=t_node.m_parent.m_parent.m_left;
			if(((t_uncle2)!=null) && t_uncle2.m_color==-1){
				t_node.m_parent.m_color=1;
				t_uncle2.m_color=1;
				t_uncle2.m_parent.m_color=-1;
				t_node=t_uncle2.m_parent;
			}else{
				if(t_node==t_node.m_parent.m_left){
					t_node=t_node.m_parent;
					this.p_RotateRight(t_node);
				}
				t_node.m_parent.m_color=1;
				t_node.m_parent.m_parent.m_color=-1;
				this.p_RotateLeft(t_node.m_parent.m_parent);
			}
		}
	}
	this.m_root.m_color=1;
	return 0;
}
c_Map.prototype.p_Set=function(t_key,t_value){
	var t_node=this.m_root;
	var t_parent=null;
	var t_cmp=0;
	while((t_node)!=null){
		t_parent=t_node;
		t_cmp=this.p_Compare(t_key,t_node.m_key);
		if(t_cmp>0){
			t_node=t_node.m_right;
		}else{
			if(t_cmp<0){
				t_node=t_node.m_left;
			}else{
				t_node.m_value=t_value;
				return false;
			}
		}
	}
	t_node=c_Node.m_new.call(new c_Node,t_key,t_value,-1,t_parent);
	if((t_parent)!=null){
		if(t_cmp>0){
			t_parent.m_right=t_node;
		}else{
			t_parent.m_left=t_node;
		}
		this.p_InsertFixup(t_node);
	}else{
		this.m_root=t_node;
	}
	return true;
}
c_Map.prototype.p_Insert=function(t_key,t_value){
	return this.p_Set(t_key,t_value);
}
function c_IntMap(){
	c_Map.call(this);
}
c_IntMap.prototype=extend_class(c_Map);
c_IntMap.m_new=function(){
	c_Map.m_new.call(this);
	return this;
}
c_IntMap.prototype.p_Compare=function(t_lhs,t_rhs){
	return t_lhs-t_rhs;
}
function c_Stack(){
	Object.call(this);
	this.m_data=[];
	this.m_length=0;
}
c_Stack.m_new=function(){
	return this;
}
c_Stack.m_new2=function(t_data){
	this.m_data=t_data.slice(0);
	this.m_length=t_data.length;
	return this;
}
c_Stack.prototype.p_Push=function(t_value){
	if(this.m_length==this.m_data.length){
		this.m_data=resize_object_array(this.m_data,this.m_length*2+10);
	}
	this.m_data[this.m_length]=t_value;
	this.m_length+=1;
}
c_Stack.prototype.p_Push2=function(t_values,t_offset,t_count){
	for(var t_i=0;t_i<t_count;t_i=t_i+1){
		this.p_Push(t_values[t_offset+t_i]);
	}
}
c_Stack.prototype.p_Push3=function(t_values,t_offset){
	this.p_Push2(t_values,t_offset,t_values.length-t_offset);
}
c_Stack.prototype.p_ToArray=function(){
	var t_t=new_object_array(this.m_length);
	for(var t_i=0;t_i<this.m_length;t_i=t_i+1){
		t_t[t_i]=this.m_data[t_i];
	}
	return t_t;
}
function c_Node(){
	Object.call(this);
	this.m_key=0;
	this.m_right=null;
	this.m_left=null;
	this.m_value=null;
	this.m_color=0;
	this.m_parent=null;
}
c_Node.m_new=function(t_key,t_value,t_color,t_parent){
	this.m_key=t_key;
	this.m_value=t_value;
	this.m_color=t_color;
	this.m_parent=t_parent;
	return this;
}
c_Node.m_new2=function(){
	return this;
}
var bb_app__displayModes=[];
var bb_app__desktopMode=null;
function bb_app_DeviceWidth(){
	return bb_app__devWidth;
}
function bb_app_DeviceHeight(){
	return bb_app__devHeight;
}
function bb_app_EnumDisplayModes(){
	var t_modes=bb_app__game.GetDisplayModes();
	var t_mmap=c_IntMap.m_new.call(new c_IntMap);
	var t_mstack=c_Stack.m_new.call(new c_Stack);
	for(var t_i=0;t_i<t_modes.length;t_i=t_i+1){
		var t_w=t_modes[t_i].width;
		var t_h=t_modes[t_i].height;
		var t_size=t_w<<16|t_h;
		if(t_mmap.p_Contains(t_size)){
		}else{
			var t_mode=c_DisplayMode.m_new.call(new c_DisplayMode,t_modes[t_i].width,t_modes[t_i].height);
			t_mmap.p_Insert(t_size,t_mode);
			t_mstack.p_Push(t_mode);
		}
	}
	bb_app__displayModes=t_mstack.p_ToArray();
	var t_mode2=bb_app__game.GetDesktopMode();
	if((t_mode2)!=null){
		bb_app__desktopMode=c_DisplayMode.m_new.call(new c_DisplayMode,t_mode2.width,t_mode2.height);
	}else{
		bb_app__desktopMode=c_DisplayMode.m_new.call(new c_DisplayMode,bb_app_DeviceWidth(),bb_app_DeviceHeight());
	}
}
var bb_graphics_renderDevice=null;
function bb_graphics_SetMatrix(t_ix,t_iy,t_jx,t_jy,t_tx,t_ty){
	bb_graphics_context.m_ix=t_ix;
	bb_graphics_context.m_iy=t_iy;
	bb_graphics_context.m_jx=t_jx;
	bb_graphics_context.m_jy=t_jy;
	bb_graphics_context.m_tx=t_tx;
	bb_graphics_context.m_ty=t_ty;
	bb_graphics_context.m_tformed=((t_ix!=1.0 || t_iy!=0.0 || t_jx!=0.0 || t_jy!=1.0 || t_tx!=0.0 || t_ty!=0.0)?1:0);
	bb_graphics_context.m_matDirty=1;
	return 0;
}
function bb_graphics_SetMatrix2(t_m){
	bb_graphics_SetMatrix(t_m[0],t_m[1],t_m[2],t_m[3],t_m[4],t_m[5]);
	return 0;
}
function bb_graphics_SetColor(t_r,t_g,t_b){
	bb_graphics_context.m_color_r=t_r;
	bb_graphics_context.m_color_g=t_g;
	bb_graphics_context.m_color_b=t_b;
	bb_graphics_renderDevice.SetColor(t_r,t_g,t_b);
	return 0;
}
function bb_graphics_SetAlpha(t_alpha){
	bb_graphics_context.m_alpha=t_alpha;
	bb_graphics_renderDevice.SetAlpha(t_alpha);
	return 0;
}
function bb_graphics_SetBlend(t_blend){
	bb_graphics_context.m_blend=t_blend;
	bb_graphics_renderDevice.SetBlend(t_blend);
	return 0;
}
function bb_graphics_SetScissor(t_x,t_y,t_width,t_height){
	bb_graphics_context.m_scissor_x=t_x;
	bb_graphics_context.m_scissor_y=t_y;
	bb_graphics_context.m_scissor_width=t_width;
	bb_graphics_context.m_scissor_height=t_height;
	bb_graphics_renderDevice.SetScissor(((t_x)|0),((t_y)|0),((t_width)|0),((t_height)|0));
	return 0;
}
function bb_graphics_BeginRender(){
	bb_graphics_renderDevice=bb_graphics_device;
	bb_graphics_context.m_matrixSp=0;
	bb_graphics_SetMatrix(1.0,0.0,0.0,1.0,0.0,0.0);
	bb_graphics_SetColor(255.0,255.0,255.0);
	bb_graphics_SetAlpha(1.0);
	bb_graphics_SetBlend(0);
	bb_graphics_SetScissor(0.0,0.0,(bb_app_DeviceWidth()),(bb_app_DeviceHeight()));
	return 0;
}
function bb_graphics_EndRender(){
	bb_graphics_renderDevice=null;
	return 0;
}
function c_BBGameEvent(){
	Object.call(this);
}
function bb_app_EndApp(){
	error("");
}
function c_TRender(){
	Object.call(this);
}
c_TRender.m_new=function(){
	return this;
}
c_TRender.m_shader2D=null;
c_TRender.m_render=null;
c_TRender.prototype.p_GraphicsInit=function(t_flags){
}
c_TRender.m_draw_list=null;
c_TRender.m_width=0;
c_TRender.m_height=0;
c_TRender.m_UpdateWorld=function(t_anim_speed){
	c_CollisionInfo.m_UpdateCollisions();
	var t_first=0;
	var t_last=0;
	var t_=c_TEntity.m_entity_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_mesh=t_.p_NextObject();
		if(((t_mesh.m_anim)!=0) && t_mesh.m_anim_update==1){
			var t_culltest=object_downcast((t_mesh),c_TMesh).p_GetCulled();
			t_first=t_mesh.m_anim_seqs_first[t_mesh.m_anim_seq];
			t_last=t_mesh.m_anim_seqs_last[t_mesh.m_anim_seq];
			var t_anim_start=0;
			if(t_mesh.m_anim_trans>0){
				t_mesh.m_anim_trans=t_mesh.m_anim_trans-1;
				if(t_mesh.m_anim_trans==1){
					t_anim_start=1;
				}
			}
			if(t_mesh.m_anim_trans>0){
				var t_r=1.0-t_mesh.m_anim_time;
				t_r=t_r/(t_mesh.m_anim_trans);
				t_mesh.m_anim_time=t_mesh.m_anim_time+t_r;
				if(t_mesh.m_anim==1 && t_culltest==false){
					c_TAnimation.m_AnimateMesh2(t_mesh,t_mesh.m_anim_time,t_first,t_last);
				}else{
					if(t_mesh.m_anim==2 && t_culltest==false){
						c_TAnimation.m_AnimateVertex(t_mesh,t_mesh.m_anim_time,t_first,t_last);
					}
				}
				if(t_anim_start==1){
					t_mesh.m_anim_time=(t_first);
				}
			}else{
				if(t_mesh.m_anim==1 && t_culltest==false){
					c_TAnimation.m_AnimateMesh(t_mesh,t_mesh.m_anim_time,t_first,t_last);
				}else{
					if(t_mesh.m_anim==2 && t_culltest==false){
						c_TAnimation.m_AnimateVertex(t_mesh,t_mesh.m_anim_time,t_first,t_last);
					}else{
						if(t_mesh.m_anim==4){
							c_TBone.m_UpdateBoneChildren(t_mesh);
							if(t_culltest==false){
								c_TAnimation.m_VertexDeform(object_downcast((t_mesh),c_TMesh));
							}
						}
					}
				}
				if(t_mesh.m_anim_mode==0){
					t_mesh.m_anim_update=0;
				}else{
					if(t_mesh.m_anim_mode==1){
						t_mesh.m_anim_time=t_mesh.m_anim_time+t_mesh.m_anim_speed*t_anim_speed;
						if(t_mesh.m_anim_time>(t_last)){
							t_mesh.m_anim_time=(t_first)+(t_mesh.m_anim_time-(t_last));
						}
					}else{
						if(t_mesh.m_anim_mode==2){
							if(t_mesh.m_anim_dir==1){
								t_mesh.m_anim_time=t_mesh.m_anim_time+t_mesh.m_anim_speed*t_anim_speed;
								if(t_mesh.m_anim_time>(t_last)){
									t_mesh.m_anim_time=t_mesh.m_anim_time-t_mesh.m_anim_speed*t_anim_speed;
									t_mesh.m_anim_dir=-1;
								}
							}
							if(t_mesh.m_anim_dir==-1){
								t_mesh.m_anim_time=t_mesh.m_anim_time-t_mesh.m_anim_speed*t_anim_speed;
								if(t_mesh.m_anim_time<(t_first)){
									t_mesh.m_anim_time=t_mesh.m_anim_time+t_mesh.m_anim_speed*t_anim_speed;
									t_mesh.m_anim_dir=1;
								}
							}
						}else{
							if(t_mesh.m_anim_mode==3){
								t_mesh.m_anim_time=t_mesh.m_anim_time+t_mesh.m_anim_speed*t_anim_speed;
								if(t_mesh.m_anim_time>(t_last)){
									t_mesh.m_anim_time=(t_first)+(t_mesh.m_anim_time-(t_last));
									t_mesh.m_anim_mode=0;
								}
							}
						}
					}
				}
			}
		}
		if((object_downcast((t_mesh),c_TMesh))!=null){
			object_downcast((t_mesh),c_TMesh).m_culled=true;
		}
	}
	return 0;
}
c_TRender.m_tris_rendered=0;
c_TRender.prototype.p_ContextReady=function(){
}
c_TRender.prototype.p_DeleteTexture=function(t_tex){
}
c_TRender.prototype.p_BindTexture=function(t_tex,t_flags){
}
c_TRender.prototype.p_BindTextureStack=function(){
	var t_=c_TTexture.m_tex_bind_stack.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_tex=t_.p_NextObject();
		if(t_tex.m_bind_flags==-255){
			c_TRender.m_render.p_DeleteTexture(t_tex);
			t_tex.p_FreeTexture_();
		}else{
			c_TRender.m_render.p_BindTexture(t_tex,t_tex.m_bind_flags);
		}
		t_tex.m_bind_flags=-1;
	}
	var t_2=c_TTexture.m_tex_bind_stack.p_ObjectEnumerator();
	while(t_2.p_HasNext()){
		var t_tex2=t_2.p_NextObject();
		if(t_tex2.m_freeMemoryAfterBind){
			t_tex2.m_pixmap.p_FreePixmap();
		}
	}
	c_TTexture.m_tex_bind_stack.p_Clear();
	return 0;
}
c_TRender.prototype.p_Reset=function(){
}
c_TRender.prototype.p_UpdateCamera=function(t_cam){
}
c_TRender.prototype.p_UpdateLight=function(t_cam,t_light){
}
c_TRender.m_render_list=null;
c_TRender.m_render_alpha_list=null;
c_TRender.m_wireframe=0;
c_TRender.prototype.p_Render=function(t_ent,t_cam){
}
c_TRender.prototype.p_Finish=function(){
}
c_TRender.prototype.p_RenderCamera=function(t_cam,t_skip){
	c_TRender.m_render.p_Reset();
	if(!((t_skip)!=0)){
		t_cam.p_Update2(t_cam);
		this.p_UpdateCamera(t_cam);
	}
	var t_=c_TLight.m_light_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_light=t_.p_NextObject();
		this.p_UpdateLight(t_cam,t_light);
	}
	c_TRender.m_render_list.p_Clear();
	c_TRender.m_render_alpha_list.p_Clear();
	var t_mesh=null;
	var t_alpha_count=0;
	var t_wireFrameIsEnabled=c_TRender.m_wireframe;
	var t_2=c_TEntity.m_entity_list.p_ObjectEnumerator();
	while(t_2.p_HasNext()){
		var t_ent=t_2.p_NextObject();
		t_mesh=object_downcast((t_ent),c_TMesh);
		if((t_mesh)!=null){
			if(t_mesh.p_Hidden()==true || t_mesh.m_brush.m_alpha==0.0){
				continue;
			}
			if(t_mesh.m_use_cam_layer && t_mesh.m_cam_layer!=t_cam || t_cam.m_use_cam_layer && t_mesh.m_cam_layer!=t_cam){
				continue;
			}
			t_mesh.p_GetBounds(false);
			var t_inview=t_cam.p_EntityInFrustum(t_mesh);
			t_mesh.m_distance_nearplane=t_inview;
			if(t_inview>0.00001){
				t_mesh.m_culled=false;
				c_TRender.m_wireframe=c_TRender.m_wireframe|((t_mesh.m_wireframe)?1:0);
				if(t_mesh.m_auto_fade==1){
					t_mesh.p_AutoFade(t_cam);
				}
				if((object_implements((t_mesh),"c_IRenderUpdate"))!=null){
					object_implements((t_mesh),"c_IRenderUpdate").p_Update2(t_cam);
				}
				if(t_mesh.p_Alpha()){
					t_mesh.m_alpha_order=t_mesh.m_distance_nearplane;
					c_TRender.m_render_alpha_list.p_AddLast3(t_mesh);
				}else{
					c_TRender.m_render.p_Render((t_mesh),t_cam);
					c_TRender.m_tris_rendered+=t_mesh.m_total_tris;
				}
				c_TRender.m_wireframe=t_wireFrameIsEnabled;
			}
		}
	}
	c_TRender.m_render_alpha_list.p_Sort(1);
	var t_3=c_TRender.m_render_alpha_list.p_ObjectEnumerator();
	while(t_3.p_HasNext()){
		t_mesh=t_3.p_NextObject();
		c_TRender.m_wireframe=c_TRender.m_wireframe|((t_mesh.m_wireframe)?1:0);
		c_TRender.m_render.p_Render((t_mesh),t_cam);
		c_TRender.m_wireframe=t_wireFrameIsEnabled;
		c_TRender.m_tris_rendered+=t_mesh.m_total_tris;
	}
	this.p_Finish();
}
c_TRender.m_temp_shader=null;
c_TRender.m_alpha_pass=0;
c_TRender.m_camera2D=null;
c_TRender.m_RenderDrawList=function(){
	if(c_TRender.m_draw_list.p_IsEmpty() || !c_TRender.m_render.p_ContextReady()){
		return;
	}
	c_TRender.m_render.p_Reset();
	c_TRender.m_shader2D.p_SetShader2D();
	c_MojoEmulationDevice.m__quadCache.p_FlushCache();
	c_TRender.m_alpha_pass=1;
	var t_wireframeIsEnabled=c_TRender.m_wireframe;
	c_TRender.m_wireframe=0;
	c_TRender.m_render.p_UpdateCamera(c_TRender.m_camera2D);
	var t_=c_TRender.m_draw_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_mesh=t_.p_NextObject();
		if(!((t_mesh)!=null)){
			continue;
		}
		if((object_implements((t_mesh),"c_IRenderUpdate"))!=null){
			object_implements((t_mesh),"c_IRenderUpdate").p_Update2(c_TRender.m_camera2D);
		}
		var t_sp=object_downcast((t_mesh),c_TSprite);
		if(t_mesh.m_is_sprite){
			t_sp.m_mat_sp.p_Scale(t_sp.m_pixel_scale[0],t_sp.m_pixel_scale[1],1.0);
			t_mesh.p_EntityFX(64);
		}
		if(t_mesh.p_Alpha()){
			t_mesh.m_alpha_order=1.0;
		}
		c_TRender.m_render.p_Render((t_mesh),c_TRender.m_camera2D);
		c_TRender.m_tris_rendered+=t_mesh.m_total_tris;
	}
	c_TRender.m_wireframe=t_wireframeIsEnabled;
	c_TRender.m_render.p_Finish();
	c_TRender.m_draw_list.p_Clear();
	c_TRender.m_camera2D.p_CameraClsMode(false,false);
	c_TRender.m_camera2D.p_CameraViewport(0,0,c_TRender.m_width,c_TRender.m_height);
	c_TRender.m_camera2D.p_SetPixelCamera();
}
c_TRender.prototype.p_RenderWorldFinish=function(){
}
c_TRender.m_RenderWorld=function(){
	c_TRender.m_tris_rendered=0;
	if(c_TRender.m_render==null || !c_TRender.m_render.p_ContextReady()){
		return;
	}
	if(c_TTexture.m_tex_bind_stack.p_Length()>0){
		c_TRender.m_render.p_BindTextureStack();
	}
	var t_=c_TCamera.m_cam_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_cam=t_.p_NextObject();
		if(t_cam.p_Hidden()==true){
			continue;
		}
		c_TShader.m_PreProcess(t_cam);
		if(object_implements((c_TShader.m_g_shader),"c_IShaderRender")==null){
			c_TRender.m_render.p_RenderCamera(t_cam,0);
		}else{
			c_TRender.m_temp_shader=c_TShader.m_g_shader;
			object_implements((c_TShader.m_g_shader),"c_IShaderRender").p_Render2(t_cam);
			c_TShader.m_g_shader=c_TRender.m_temp_shader;
		}
		c_TShader.m_PostProcess(t_cam);
	}
	c_TRender.m_RenderDrawList();
	c_TRender.m_render.p_RenderWorldFinish();
}
c_TRender.m_vbo_enabled=false;
c_TRender.prototype.p_UpdateVBO=function(t_surface){
}
c_TRender.prototype.p_ClearErrors=function(){
	return 1;
}
c_TRender.prototype.p_GetVersion=function(){
}
function c_OpenglES20(){
	c_TRender.call(this);
	this.m_cam_matrix_upload=0;
	this.m_light=new_object_array(8);
	this.m_t_array=new_number_array(16);
	this.m_vmat_array=new_number_array(16);
	this.m_pvmat_array=new_number_array(16);
	this.implments={c_IShader2D:1};
}
c_OpenglES20.prototype=extend_class(c_TRender);
c_OpenglES20.m_new=function(){
	c_TRender.m_new.call(this);
	c_TRender.m_shader2D=(this);
	return this;
}
c_OpenglES20.prototype.p_ContextReady=function(){
	return true;
}
c_OpenglES20.m_last_texture=null;
c_OpenglES20.m_last_surf=null;
c_OpenglES20.m_last_shader=0;
c_OpenglES20.m_last_tex_count=0;
c_OpenglES20.prototype.p_ResetLights=function(){
	this.m_light=new_object_array(8);
	var t_i=0;
	var t_=c_TLight.m_light_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_li=t_.p_NextObject();
		this.m_light[t_i]=t_li;
		t_i+=1;
		if(t_i>8){
			break;
		}
	}
	for(var t_j=t_i;t_j<=7;t_j=t_j+1){
		this.m_light[t_j]=null;
	}
}
c_OpenglES20.m_last_effect=null;
c_OpenglES20.prototype.p_Reset=function(){
	c_OpenglES20.m_last_texture=null;
	c_OpenglES20.m_last_surf=null;
	c_TRender.m_alpha_pass=0;
	c_OpenglES20.m_last_shader=-1;
	this.m_cam_matrix_upload=0;
	c_OpenglES20.m_last_tex_count=-1;
	this.p_ResetLights();
	c_OpenglES20.m_last_effect.p_SetNull();
	c_TShader.m_DefaultShader();
}
c_OpenglES20.prototype.p_Finish=function(){
}
c_OpenglES20.m_alpha_list=null;
c_OpenglES20.m_effect=null;
c_OpenglES20.m_total_errors=0;
c_OpenglES20.m_GetGLError=function(){
	return 0;
}
c_OpenglES20.prototype.p_UpdateVBO=function(t_surf){
	if(t_surf.m_vbo_id[0]==0){
		t_surf.m_vbo_id[0]=gl.createBuffer();
		t_surf.m_vbo_id[1]=gl.createBuffer();
		t_surf.m_vbo_id[2]=gl.createBuffer();
		t_surf.m_vbo_id[3]=gl.createBuffer();
		t_surf.m_vbo_id[4]=gl.createBuffer();
		t_surf.m_vbo_id[5]=gl.createBuffer();
	}
	if(t_surf.m_vbo_id[0]==0){
		return 0;
	}
	if(t_surf.m_reset_vbo==-1){
		t_surf.m_reset_vbo=255;
	}
	if(((t_surf.m_reset_vbo&1)!=0) || ((t_surf.m_reset_vbo&2)!=0) || ((t_surf.m_reset_vbo&4)!=0) || ((t_surf.m_reset_vbo&8)!=0)){
		if(t_surf.m_vbo_dyn && !((t_surf.m_vert_anim).length!=0)){
			_glBindBuffer(34962,t_surf.m_vbo_id[0]);
			if(t_surf.m_reset_vbo!=255){
				_glBufferSubData(34962,0,t_surf.m_no_verts*64,t_surf.m_vert_data.m_buf);
			}else{
				_glBufferData(34962,t_surf.m_no_verts*64,t_surf.m_vert_data.m_buf,35048);
			}
		}else{
			if(t_surf.m_vbo_dyn && ((t_surf.m_vert_anim).length!=0) && ((t_surf.m_reset_vbo&1)!=0)){
				_glBindBuffer(34962,t_surf.m_vbo_id[4]);
				if(t_surf.m_reset_vbo!=255){
					_glBufferSubData(34962,0,t_surf.m_no_verts*12,t_surf.m_vert_anim[t_surf.m_anim_frame].m_buf);
				}else{
					_glBufferData(34962,t_surf.m_no_verts*12,t_surf.m_vert_anim[t_surf.m_anim_frame].m_buf,35048);
				}
			}else{
				_glBindBuffer(34962,t_surf.m_vbo_id[0]);
				_glBufferData(34962,t_surf.m_no_verts*64,t_surf.m_vert_data.m_buf,35044);
			}
		}
	}
	if((t_surf.m_reset_vbo&16)!=0){
		_glBindBuffer(34963,t_surf.m_vbo_id[5]);
		if(t_surf.m_reset_vbo!=255){
			_glBufferSubData(34963,0,t_surf.m_no_tris*6,t_surf.m_tris.m_buf);
		}else{
			_glBufferData(34963,t_surf.m_no_tris*6,t_surf.m_tris.m_buf,35044);
		}
	}
	if((c_OpenglES20.m_GetGLError())!=0){
		print("vbo error");
	}
	t_surf.m_reset_vbo=0;
	return 0;
}
c_OpenglES20.m__usePerPixelLighting=0;
c_OpenglES20.m__nullTexture=null;
c_OpenglES20.prototype.p_ClearErrors=function(){
	return 0;
}
c_OpenglES20.prototype.p_Render=function(t_ent,t_cam){
	var t_mesh=object_downcast((t_ent),c_TMesh);
	if(!((t_mesh)!=null)){
		return;
	}
	var t_name=t_ent.p_EntityName();
	var t_shader=null;
	t_shader=object_downcast((c_TShader.m_g_shader),c_TShaderGLSL);
	if(!((t_shader.m_active)!=0)){
		return;
	}
	if(((object_downcast((t_ent.m_shader_brush),c_TShaderGLSL))!=null) && !t_shader.m_override && ((t_ent.m_shader_brush.m_active)!=0)){
		t_shader=object_downcast((t_ent.m_shader_brush),c_TShaderGLSL);
		if((object_implements((t_shader),"c_IShaderEntity"))!=null){
			object_implements((t_shader),"c_IShaderEntity").p_RenderEntity(t_cam,t_ent);
			if(t_shader.m_shader_id!=c_OpenglES20.m_last_shader){
				c_OpenglES20.m_last_shader=t_shader.m_shader_id;
			}
			return;
		}
	}
	var t_anim_surf2=null;
	var t_surf=null;
	var t_ccc=0;
	var t_lightflag=1;
	var t_temp_list=t_mesh.m_surf_list;
	c_OpenglES20.m_alpha_list.p_Clear();
	if((t_cam.m_draw2D)!=0){
		c_OpenglES20.m_effect.m_use_depth_test=0;
	}
	for(var t_alphaloop=c_TRender.m_alpha_pass;t_alphaloop<=1;t_alphaloop=t_alphaloop+1){
		var t_=t_temp_list.p_ObjectEnumerator();
		while(t_.p_HasNext()){
			t_surf=t_.p_NextObject();
			t_ccc+=1;
			if(t_surf.m_no_verts==0){
				continue;
			}
			if(t_surf.m_alpha_enable && t_alphaloop<1){
				c_OpenglES20.m_alpha_list.p_AddLast2(t_surf);
				continue;
			}
			var t_vbo=1;
			if(!c_TRender.m_vbo_enabled){
				t_vbo=0;
				if(t_surf.m_vbo_id[0]!=0){
					gl.deleteBuffer(t_surf.m_vbo_id[0]);
					gl.deleteBuffer(t_surf.m_vbo_id[1]);
					gl.deleteBuffer(t_surf.m_vbo_id[2]);
					gl.deleteBuffer(t_surf.m_vbo_id[3]);
					gl.deleteBuffer(t_surf.m_vbo_id[4]);
					gl.deleteBuffer(t_surf.m_vbo_id[5]);
				}
			}
			if((t_vbo)!=0){
				if(t_surf.m_reset_vbo!=0){
					this.p_UpdateVBO(t_surf);
				}else{
					if(t_surf.m_vbo_id[0]==0){
						t_surf.m_reset_vbo=-1;
						this.p_UpdateVBO(t_surf);
					}
				}
			}
			if((t_mesh.m_anim)!=0){
				t_anim_surf2=t_mesh.m_anim_surf[t_surf.m_surf_id];
				if(((t_vbo)!=0) && ((t_anim_surf2)!=null)){
					t_mesh.p_UpdateVertexAnimFrame(t_anim_surf2,t_surf);
					if(t_anim_surf2.m_reset_vbo!=0){
						this.p_UpdateVBO(t_anim_surf2);
					}else{
						if(t_anim_surf2.m_vbo_id[0]==0){
							t_anim_surf2.m_reset_vbo=-1;
							this.p_UpdateVBO(t_anim_surf2);
						}
					}
				}
			}
			c_OpenglES20.m_effect.p_UpdateEffect(t_surf,t_ent,t_cam);
			var t_tex_count=t_ent.m_brush.m_no_texs;
			if(t_surf.m_brush.m_no_texs>t_tex_count){
				t_tex_count=t_surf.m_brush.m_no_texs;
			}
			if(t_tex_count>t_shader.m_MAX_TEXTURES-1){
				t_tex_count=t_shader.m_MAX_TEXTURES-1;
			}
			if(object_downcast((t_shader),c_FullShader)!=null){
				t_shader=c_FullShader.m_GetShader(c_OpenglES20.m__usePerPixelLighting|c_OpenglES20.m_effect.m_use_perpixellighting,1,t_tex_count);
				if(c_OpenglES20.m_effect.m_use_full_bright>0){
					t_shader=c_FullShader.m_fastbrightshader;
				}
			}
			if(!((t_shader.m_u)!=null)){
				continue;
			}
			if(t_shader.m_shader_id!=c_OpenglES20.m_last_shader){
				gl.useProgram(t_shader.m_shader_id);
			}
			t_shader.p_Update12();
			var t_skip_sprite_state=false;
			if(c_OpenglES20.m_last_surf==t_surf && t_shader.m_shader_id==c_OpenglES20.m_last_shader){
				t_skip_sprite_state=true;
			}else{
				c_OpenglES20.m_last_surf=t_surf;
			}
			if(t_skip_sprite_state==false){
				if(false && ((c_OpenglES20.m_GetGLError())!=0)){
					print("*pre vbos");
				}
				if((t_vbo)!=0){
					var t_bind=false;
					if(!(((t_mesh.m_anim_render)!=0) || t_surf.m_vbo_dyn)){
						gl.enableVertexAttribArray(t_shader.m_u.m_vertcoords);
						_glBindBuffer(34962,t_surf.m_vbo_id[0]);
						gl.vertexAttribPointer(t_shader.m_u.m_vertcoords,3,5126,false,64,0);
						t_bind=true;
					}
					if(t_shader.m_u.m_normals!=-1){
						gl.enableVertexAttribArray(t_shader.m_u.m_normals);
						if(!t_bind){
							_glBindBuffer(34962,t_surf.m_vbo_id[0]);
						}
						gl.vertexAttribPointer(t_shader.m_u.m_normals,3,5126,false,64,16);
						t_bind=true;
					}
					if(t_shader.m_u.m_colors!=-1){
						gl.enableVertexAttribArray(t_shader.m_u.m_colors);
						if(!t_bind){
							_glBindBuffer(34962,t_surf.m_vbo_id[0]);
						}
						gl.vertexAttribPointer(t_shader.m_u.m_colors,4,5126,false,64,32);
						t_bind=true;
					}
					_glBindBuffer(34963,t_surf.m_vbo_id[5]);
				}else{
					_glBindBuffer(34962,0);
					_glBindBuffer(34963,0);
					print("*** Non-VBO disabled");
				}
			}
			if(((t_mesh.m_anim_render)!=0) || t_surf.m_vbo_dyn || ((t_anim_surf2)!=null)){
				gl.enableVertexAttribArray(t_shader.m_u.m_vertcoords);
				if(((t_anim_surf2)!=null) && ((t_anim_surf2.m_vert_anim).length!=0)){
					_glBindBuffer(34962,t_anim_surf2.m_vbo_id[4]);
					gl.vertexAttribPointer(t_shader.m_u.m_vertcoords,3,5126,false,12,0);
				}else{
					if(((t_mesh.m_anim_render)!=0) && ((t_anim_surf2)!=null)){
						_glBindBuffer(34962,t_anim_surf2.m_vbo_id[0]);
						gl.vertexAttribPointer(t_shader.m_u.m_vertcoords,3,5126,false,64,0);
					}else{
						if(t_surf.m_vbo_dyn){
							_glBindBuffer(34962,t_surf.m_vbo_id[0]);
							gl.vertexAttribPointer(t_shader.m_u.m_vertcoords,3,5126,false,64,0);
						}
					}
				}
			}
			if(false && ((c_OpenglES20.m_GetGLError())!=0)){
				print("*vbos");
			}
			if(t_shader.m_u.m_flags!=-1){
				gl.uniform1f(t_shader.m_u.m_flags,(c_OpenglES20.m_effect.m_fx));
			}
			if(c_OpenglES20.m_effect.m_use_vertex_colors<0){
				c_OpenglES20.m_effect.m_use_vertex_colors=0;
			}
			if(t_shader.m_u.m_colorflag!=-1){
				gl.uniform1f(t_shader.m_u.m_colorflag,(c_OpenglES20.m_effect.m_use_vertex_colors));
			}
			if(c_OpenglES20.m_effect.m_use_full_bright<0){
				c_OpenglES20.m_effect.m_use_full_bright=0;
			}
			if(t_shader.m_u.m_lightflag!=-1){
				gl.uniform1f(t_shader.m_u.m_lightflag,1.0-(c_OpenglES20.m_effect.m_use_full_bright));
			}
			if(t_shader.m_u.m_ambient_color!=-1){
				_glUniform4fv(t_shader.m_u.m_ambient_color,1,c_OpenglES20.m_effect.m_ambient);
			}
			if(t_shader.m_u.m_base_color!=-1){
				_glUniform4fv(t_shader.m_u.m_base_color,1,c_OpenglES20.m_effect.m_diffuse);
			}
			if(t_shader.m_u.m_shininess!=-1){
				gl.uniform1f(t_shader.m_u.m_shininess,c_OpenglES20.m_effect.m_shine);
			}
			if(!t_skip_sprite_state){
				if((c_OpenglES20.m_effect.m_use_flatshade)!=0){
				}
				if(c_OpenglES20.m_effect.m_use_backface_culling!=c_OpenglES20.m_last_effect.m_use_backface_culling){
					if(c_OpenglES20.m_effect.m_use_backface_culling==0){
						gl.disable(2884);
					}else{
						gl.enable(2884);
					}
				}
				if(c_OpenglES20.m_effect.m_blend>-1){
					var t_1=c_OpenglES20.m_effect.m_blend;
					if(t_1==0){
						gl.blendFuncSeparate(770,771,1,771);
					}else{
						if(t_1==1){
							gl.blendFunc(770,771);
						}else{
							if(t_1==2){
								gl.blendFunc(774,0);
							}else{
								if(t_1==3){
									gl.blendFunc(770,1);
								}else{
									if(t_1==4){
										gl.blendFunc(1,1);
									}
								}
							}
						}
					}
				}
				if(c_OpenglES20.m_effect.m_blend!=c_OpenglES20.m_last_effect.m_blend){
					if(c_OpenglES20.m_effect.m_blend>-1){
						gl.enable(3042);
					}else{
						gl.disable(3042);
					}
				}
				if(c_OpenglES20.m_effect.m_use_depth_test!=c_OpenglES20.m_last_effect.m_use_depth_test){
					if(c_OpenglES20.m_effect.m_use_depth_test==0){
						gl.disable(2929);
					}else{
						gl.enable(2929);
					}
				}
				if(c_OpenglES20.m_effect.m_use_depth_write!=c_OpenglES20.m_last_effect.m_use_depth_write){
					if(c_OpenglES20.m_effect.m_use_depth_write==0){
						gl.depthMask(false);
					}else{
						gl.depthMask(true);
					}
				}
				if(c_OpenglES20.m_effect.m_use_alpha_test>0){
					if(t_shader.m_u.m_alphaflag!=-1){
						gl.uniform1f(t_shader.m_u.m_alphaflag,0.5);
					}
				}else{
					if(t_shader.m_u.m_alphaflag!=-1){
						gl.uniform1f(t_shader.m_u.m_alphaflag,0.0);
					}
				}
				if(c_OpenglES20.m_effect.m_use_fog>0){
					if(t_shader.m_u.m_fogflag!=-1){
						gl.uniform1i(t_shader.m_u.m_fogflag,t_cam.m_fog_mode);
					}
					if(t_shader.m_u.m_fog_color!=-1){
						_glUniform4fv(t_shader.m_u.m_fog_color,1,[t_cam.m_fog_r,t_cam.m_fog_g,t_cam.m_fog_b,1.0]);
					}
					if(t_shader.m_u.m_fog_range!=-1){
						_glUniform2fv(t_shader.m_u.m_fog_range,1,[t_cam.m_fog_range_near,t_cam.m_fog_range_far]);
					}
				}else{
					if(t_shader.m_u.m_fogflag!=-1){
						gl.uniform1i(t_shader.m_u.m_fogflag,0);
					}
				}
			}
			if(t_tex_count<c_OpenglES20.m_last_tex_count){
				for(var t_i=t_tex_count;t_i<=t_shader.m_MAX_TEXTURES-1;t_i=t_i+1){
					gl.activeTexture(33984+t_i);
					_glBindTexture(3553,0);
				}
			}
			var t_bindTexCoords0=false;
			var t_bindTexCoords1=false;
			for(var t_ix=t_tex_count-1;t_ix>=0;t_ix=t_ix+-1){
				var t_texture=null;
				var t_tex_flags=0;
				var t_tex_blend=0;
				var t_tex_coords=0;
				var t_tex_u_scale=1.0;
				var t_tex_v_scale=1.0;
				var t_tex_u_pos=.0;
				var t_tex_v_pos=.0;
				var t_tex_ang=.0;
				var t_tex_cube_mode=0;
				var t_frame=0;
				var t_tex_smooth=0;
				if(t_surf.m_brush.m_tex[t_ix]!=null || t_ent.m_brush.m_tex[t_ix]!=null){
					if(t_ent.m_brush.m_tex[t_ix]!=null){
						t_texture=t_ent.m_brush.m_tex[t_ix];
						t_tex_flags=t_ent.m_brush.m_tex[t_ix].m_flags;
						t_tex_blend=t_ent.m_brush.m_tex[t_ix].m_blend;
						t_tex_coords=t_ent.m_brush.m_tex[t_ix].m_coords;
						t_tex_u_scale=t_ent.m_brush.m_tex[t_ix].m_u_scale;
						t_tex_v_scale=t_ent.m_brush.m_tex[t_ix].m_v_scale;
						t_tex_u_pos=t_ent.m_brush.m_tex[t_ix].m_u_pos;
						t_tex_v_pos=t_ent.m_brush.m_tex[t_ix].m_v_pos;
						t_tex_ang=t_ent.m_brush.m_tex[t_ix].m_angle;
						t_tex_cube_mode=t_ent.m_brush.m_tex[t_ix].m_cube_mode;
						t_frame=t_ent.m_brush.m_tex[t_ix].m_tex_frame;
						t_tex_smooth=((t_ent.m_brush.m_tex[t_ix].m_tex_smooth)?1:0);
					}else{
						t_texture=t_surf.m_brush.m_tex[t_ix];
						t_tex_flags=t_surf.m_brush.m_tex[t_ix].m_flags;
						t_tex_blend=t_surf.m_brush.m_tex[t_ix].m_blend;
						t_tex_coords=t_surf.m_brush.m_tex[t_ix].m_coords;
						t_tex_u_scale=t_surf.m_brush.m_tex[t_ix].m_u_scale;
						t_tex_v_scale=t_surf.m_brush.m_tex[t_ix].m_v_scale;
						t_tex_u_pos=t_surf.m_brush.m_tex[t_ix].m_u_pos;
						t_tex_v_pos=t_surf.m_brush.m_tex[t_ix].m_v_pos;
						t_tex_ang=t_surf.m_brush.m_tex[t_ix].m_angle;
						t_tex_cube_mode=t_surf.m_brush.m_tex[t_ix].m_cube_mode;
						t_frame=t_surf.m_brush.m_tex[t_ix].m_tex_frame;
						t_tex_smooth=((t_surf.m_brush.m_tex[t_ix].m_tex_smooth)?1:0);
					}
					if(t_texture.m_width==0){
						t_texture=c_OpenglES20.m__nullTexture;
					}
					if(t_texture==c_OpenglES20.m_last_texture && t_ix==0){
					}else{
						c_OpenglES20.m_last_texture=t_texture;
						gl.activeTexture(33984+t_ix);
						_glBindTexture(3553,t_texture.m_gltex[0]);
						if(t_shader.m_u.m_texture[t_ix]!=-1){
							gl.uniform1i(t_shader.m_u.m_texture[t_ix],t_ix);
						}
						if((t_tex_flags&8)!=0){
							if((t_tex_smooth)!=0){
								gl.texParameteri(3553,10240,9729);
								gl.texParameteri(3553,10241,9985);
							}else{
								gl.texParameteri(3553,10240,9728);
								gl.texParameteri(3553,10241,9984);
							}
						}else{
							if((t_tex_smooth)!=0){
								gl.texParameteri(3553,10240,9729);
								gl.texParameteri(3553,10241,9729);
							}else{
								gl.texParameteri(3553,10240,9728);
								gl.texParameteri(3553,10241,9728);
							}
						}
						if((t_tex_flags&16)!=0){
							gl.texParameteri(3553,10242,33071);
						}else{
							gl.texParameteri(3553,10242,10497);
						}
						if((t_tex_flags&32)!=0){
							gl.texParameteri(3553,10243,33071);
						}else{
							gl.texParameteri(3553,10243,10497);
						}
					}
					if(t_tex_coords==0){
						t_bindTexCoords0=true;
					}
					if(t_tex_coords==1){
						t_bindTexCoords1=true;
					}
					if(t_shader.m_u.m_tex_position[t_ix]!=-1){
						gl.uniform2f(t_shader.m_u.m_tex_position[t_ix],t_tex_u_pos,t_tex_v_pos);
					}
					if(t_shader.m_u.m_tex_rotation[t_ix]!=-1){
						gl.uniform2f(t_shader.m_u.m_tex_rotation[t_ix],Math.cos((t_tex_ang)*D2R),Math.sin((t_tex_ang)*D2R));
					}
					if(t_shader.m_u.m_tex_scale[t_ix]!=-1){
						gl.uniform2f(t_shader.m_u.m_tex_scale[t_ix],t_tex_u_scale,t_tex_v_scale);
					}
					if(t_shader.m_u.m_tex_blend[t_ix]!=-1){
						gl.uniform2f(t_shader.m_u.m_tex_blend[t_ix],(t_tex_blend),(t_tex_blend));
					}
					if(t_ix==0 && t_shader.m_u.m_texfx_normal[t_ix]!=-1){
						gl.uniform1f(t_shader.m_u.m_texfx_normal[t_ix],(((t_tex_flags&1024)>0)?1:0));
					}
					if(t_shader.m_u.m_vertCoordSet[t_ix]!=-1){
						gl.uniform1f(t_shader.m_u.m_vertCoordSet[t_ix],(t_tex_coords));
					}
					if(false && ((c_OpenglES20.m_GetGLError())!=0)){
						print("*tex "+String(t_ix));
					}
				}
			}
			if(t_tex_count==0){
				c_OpenglES20.m_last_texture=null;
				gl.activeTexture(33984);
				t_bindTexCoords0=false;
				t_bindTexCoords1=false;
			}
			if(((t_vbo)!=0) && !t_skip_sprite_state){
				if(t_shader.m_u.m_texcoords0!=-1 && t_bindTexCoords0==true){
					gl.enableVertexAttribArray(t_shader.m_u.m_texcoords0);
					_glBindBuffer(34962,t_surf.m_vbo_id[0]);
					gl.vertexAttribPointer(t_shader.m_u.m_texcoords0,2,5126,false,64,48);
				}
				if(t_shader.m_u.m_texcoords1!=-1 && t_bindTexCoords1==true){
					gl.enableVertexAttribArray(t_shader.m_u.m_texcoords1);
					_glBindBuffer(34962,t_surf.m_vbo_id[0]);
					gl.vertexAttribPointer(t_shader.m_u.m_texcoords1,2,5126,false,64,56);
				}
				if(t_bindTexCoords0==false && t_shader.m_u.m_texcoords0!=-1){
					gl.disableVertexAttribArray(t_shader.m_u.m_texcoords0);
				}
				if(t_bindTexCoords1==false && t_shader.m_u.m_texcoords1!=-1){
					gl.disableVertexAttribArray(t_shader.m_u.m_texcoords1);
				}
			}else{
				if(!t_skip_sprite_state){
				}
			}
			if(t_shader.m_u.m_texflag!=-1){
				gl.uniform1f(t_shader.m_u.m_texflag,(t_tex_count));
			}
			if(false && ((c_OpenglES20.m_GetGLError())!=0)){
				print("*tex2");
			}
			if(c_OpenglES20.m_last_shader!=t_shader.m_shader_id){
				for(var t_li=0;t_li<=t_shader.m_MAX_LIGHTS-1;t_li=t_li+1){
					if((this.m_light[t_li])!=null){
						if(t_shader.m_u.m_light_type[t_li]!=-1){
							gl.uniform1f(t_shader.m_u.m_light_type[t_li],(this.m_light[t_li].m_light_type));
						}
						this.m_light[t_li].m_mat.p_ToArray2(this.m_t_array);
						if(t_shader.m_u.m_light_matrix[t_li]!=-1){
							_glUniformMatrix4fv(t_shader.m_u.m_light_matrix[t_li],1,false,this.m_t_array);
						}
						if(t_shader.m_u.m_light_att[t_li]!=-1){
							_glUniform4fv(t_shader.m_u.m_light_att[t_li],1,[this.m_light[t_li].m_const_att,this.m_light[t_li].m_lin_att,this.m_light[t_li].m_quad_att,this.m_light[t_li].m_actual_range]);
						}
						if(t_shader.m_u.m_light_color[t_li]!=-1){
							_glUniform4fv(t_shader.m_u.m_light_color[t_li],1,[this.m_light[t_li].m_red,this.m_light[t_li].m_green,this.m_light[t_li].m_blue,1.0]);
						}
						if(t_shader.m_u.m_light_spot[t_li]!=-1){
							_glUniform3fv(t_shader.m_u.m_light_spot[t_li],1,[Math.cos((this.m_light[t_li].m_outer_ang)*D2R),Math.cos((this.m_light[t_li].m_inner_ang)*D2R),this.m_light[t_li].m_spot_exp]);
						}
					}else{
						if(t_shader.m_u.m_light_type[t_li]!=-1){
							gl.uniform1f(t_shader.m_u.m_light_type[t_li],0.0);
						}
						if(t_shader.m_u.m_light_color[t_li]!=-1){
							_glUniform4fv(t_shader.m_u.m_light_color[t_li],1,[0.0,0.0,0.0,1.0]);
						}
					}
				}
			}
			if(t_mesh.m_is_sprite==false){
				t_mesh.m_mat.p_ToArray2(this.m_t_array);
				if(t_shader.m_u.m_m_matrix!=-1){
					_glUniformMatrix4fv(t_shader.m_u.m_m_matrix,1,false,this.m_t_array);
				}
			}else{
				object_downcast((t_mesh),c_TSprite).m_mat_sp.p_ToArray2(this.m_t_array);
				if(t_shader.m_u.m_m_matrix!=-1){
					_glUniformMatrix4fv(t_shader.m_u.m_m_matrix,1,false,this.m_t_array);
				}
			}
			if(t_shader.m_u.m_v_matrix!=-1){
				_glUniformMatrix4fv(t_shader.m_u.m_v_matrix,1,false,this.m_vmat_array);
			}
			if(t_shader.m_u.m_p_matrix!=-1){
				_glUniformMatrix4fv(t_shader.m_u.m_p_matrix,1,false,this.m_pvmat_array);
			}
			if(t_shader.m_u.m_scaleInv!=-1){
				_glUniform3fv(t_shader.m_u.m_scaleInv,1,[1.0/t_mesh.m_gsx,1.0/t_mesh.m_gsy,1.0/t_mesh.m_gsz]);
			}
			if(false && ((c_OpenglES20.m_GetGLError())!=0)){
				print("*mats flags");
			}
			if((c_TRender.m_wireframe)!=0){
				if(!((t_vbo)!=0)){
					gl.drawElements(2,t_surf.m_no_tris*3,5123,t_surf.m_tris.m_buf);
				}
				if((t_vbo)!=0){
					gl.drawElements(2,t_surf.m_no_tris*3,5123,0);
				}
			}else{
				if((t_vbo)!=0){
					gl.drawElements(4,t_surf.m_no_tris*3,5123,0);
				}else{
					gl.drawElements(4,t_surf.m_no_tris*3,5123,t_surf.m_tris.m_buf);
				}
			}
			if(false && ((c_OpenglES20.m_GetGLError())!=0)){
				print("*glDrawElements");
			}
			c_OpenglES20.m_last_tex_count=t_tex_count;
			c_OpenglES20.m_last_effect.p_Overwrite4(c_OpenglES20.m_effect);
			c_OpenglES20.m_last_shader=t_shader.m_shader_id;
		}
		if(!((c_OpenglES20.m_alpha_list)!=null)){
			break;
		}
		t_temp_list=(c_OpenglES20.m_alpha_list);
	}
	t_temp_list=null;
	this.p_ClearErrors();
}
c_OpenglES20.prototype.p_GetVersion=function(){
	var t_webgl="";
	var t_st="";
	if(!((window.WebGLRenderingContext)!=0)){
		error("** WebGL not found. Please upgrade or check browser options. \n\n");
	}
	if(!((CheckWebGLContext())!=0)){
		error("** WebGL Context not found. Please upgrade or check browser options. \n\n");
	}
	var t_s=_glGetString(7938);
	t_webgl=t_s.split(" ")[0];
	var t_num=0;
	for(var t_i=0;t_i<=t_s.length-1;t_i=t_i+1){
		if(t_s.charCodeAt(t_i)>47 && t_s.charCodeAt(t_i)<58){
			t_st=t_st+String.fromCharCode(t_s.charCodeAt(t_i));
			if(t_num==0){
				t_num=1;
			}
		}else{
			if(t_s.charCodeAt(t_i)==46){
				if(t_num==2){
					break;
				}
				t_st=t_st+String.fromCharCode(t_s.charCodeAt(t_i));
				t_num=2;
			}else{
				if(t_num!=0){
					break;
				}
			}
		}
	}
	var t_sn=1.0;
	if(string_trim(t_webgl.toLowerCase())=="webgl"){
		t_sn=-1.0;
	}
	return ((parseFloat(t_st)*10.0)|0)/10.0*t_sn;
}
c_OpenglES20.prototype.p_EnableStates=function(){
	gl.enable(2929);
	gl.depthMask(true);
	_glClearDepthf(1.0);
	gl.depthFunc(515);
	gl.enable(2884);
	gl.enable(3089);
	gl.enable(3042);
}
c_OpenglES20.prototype.p_GraphicsInit=function(t_flags){
	var t_version=((this.p_GetVersion())|0);
	if((t_version)>0.0){
		print("..OPENGL "+String(t_version));
	}else{
		print("..WEBGL "+String(-t_version));
	}
	if((t_version)<1.999 && (t_version)>0.0){
		error("Requires OpenGL 2.0 or higher");
	}
	c_TPixmapGL.m_Init();
	c_FrameBufferGL.m_supportFBO=1;
	c_TTexture.m_TextureFilter("",9);
	c_TRender.m_width=bb_app_DeviceWidth();
	c_TRender.m_height=bb_app_DeviceHeight();
	c_TRender.m_vbo_enabled=true;
	c_OpenglES20.m__usePerPixelLighting=(((t_flags&2)>0)?1:0);
	this.p_EnableStates();
	c_TEntity.m_global_mat.p_LoadIdentity();
	this.p_ClearErrors();
	c_TShader.m_LoadDefaultShader2(c_FullShader.m_new.call(new c_FullShader));
	c_OpenglES20.m__nullTexture=bb_functions_CreateTexture(1,1,1,1);
	if(gl.getError()!=0){
		return 0;
	}
	return 1;
}
c_OpenglES20.prototype.p_DeleteTexture=function(t_tex){
	if((t_tex.m_gltex[0])!=0){
		gl.deleteTexture(t_tex.m_gltex[0]);
	}
	t_tex.m_gltex[0]=0;
	return 0;
}
c_OpenglES20.prototype.p_BindTexture=function(t_tex,t_flags){
	c_TRender.m_render.p_ClearErrors();
	if((t_flags&4)!=0){
	}
	var t_width=t_tex.m_pixmap.m_width;
	var t_height=t_tex.m_pixmap.m_height;
	if(t_width==0 || t_height==0){
		return t_tex;
	}
	if(!((t_tex.m_gltex[0])!=0)){
		t_tex.m_gltex[0]=gl.createTexture();
	}else{
		if((t_tex.m_pixmap.m_bind)!=0){
			return t_tex;
		}
	}
	_glBindTexture(3553,t_tex.m_gltex[0]);
	if((t_flags&8)!=0){
		gl.texParameteri(3553,10240,9729);
		gl.texParameteri(3553,10241,9985);
	}else{
		gl.texParameteri(3553,10240,9728);
		gl.texParameteri(3553,10241,9728);
	}
	if((t_flags&16)!=0){
		gl.texParameteri(3553,10242,33071);
	}else{
		gl.texParameteri(3553,10242,10497);
	}
	if((t_flags&32)!=0){
		gl.texParameteri(3553,10243,33071);
	}else{
		gl.texParameteri(3553,10243,10497);
	}
	var t_mipmap=0;
	var t_mip_level=0;
	if((t_flags&8)!=0){
		t_mipmap=1;
	}
	var t_pix=object_downcast((t_tex.m_pixmap),c_TPixmapGL);
	do{
		gl.pixelStorei(3317,1);
		gl.texImage2D(3553,t_mip_level,6408,6408,5121,t_pix.m_pixels);
		var t_err=gl.getError();
		if(t_err!=0){
			error("** out of texture memory ** "+String(t_err));
		}
		if(!((t_mipmap)!=0) || (t_width==1 || t_height==1)){
			break;
		}
		if(t_width>1){
			t_width=(((t_width)*0.5)|0);
		}
		if(t_height>1){
			t_height=(((t_height)*0.5)|0);
		}
		if(t_tex.m_resize_smooth){
			t_pix=object_downcast((t_pix.p_ResizePixmap(t_width,t_height)),c_TPixmapGL);
		}else{
			t_pix=object_downcast((t_pix.p_ResizePixmapNoSmooth(t_width,t_height)),c_TPixmapGL);
		}
		t_mip_level+=1;
	}while(!(false));
	t_tex.m_no_mipmaps=t_mip_level;
	t_tex.m_pixmap.p_SetBind();
	return t_tex;
}
c_OpenglES20.prototype.p_BindTextureStack=function(){
	c_TRender.prototype.p_BindTextureStack.call(this);
	var t_=c_FrameBufferGL.m_fboStack.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_fbo=t_.p_NextObject();
		c_FrameBufferGL.m_BindFBO(object_downcast((t_fbo),c_FrameBufferGL));
	}
	c_FrameBufferGL.m_fboStack.p_Clear();
	return 0;
}
c_OpenglES20.prototype.p_UpdateLight=function(t_cam,t_light){
	return 0;
}
c_OpenglES20.prototype.p_UpdateCamera=function(t_cam){
	if(((c_FrameBufferGL.m_framebuffer_active)!=null) && c_FrameBufferGL.m_framebuffer_active.m_texture.m_width>0){
		var t_fw=(c_FrameBufferGL.m_framebuffer_active.m_texture.m_width);
		var t_fh=(c_FrameBufferGL.m_framebuffer_active.m_texture.m_height);
		var t_scalew=1.0;
		var t_scaleh=1.0;
		if(t_cam.m_vwidth>t_cam.m_vheight){
			t_scaleh=c_FrameBufferGL.m_framebuffer_active.m_UVH;
		}else{
			t_scalew=c_FrameBufferGL.m_framebuffer_active.m_UVW;
		}
		if(t_fw<(t_cam.m_vwidth) || t_fh<(t_cam.m_vheight)){
			gl.viewport(t_cam.m_vx,t_cam.m_vy,((t_fw*t_scalew)|0),((t_fh*t_scaleh)|0));
			gl.scissor(t_cam.m_vx,t_cam.m_vy,((t_fw*t_scalew)|0),((t_fh*t_scaleh)|0));
		}else{
			gl.viewport(t_cam.m_vx,t_cam.m_vy,t_cam.m_vwidth,t_cam.m_vheight);
			gl.scissor(t_cam.m_vx,t_cam.m_vy,t_cam.m_vwidth,t_cam.m_vheight);
		}
	}else{
		if((t_cam.m_draw2D)!=0){
			gl.viewport(0,0,bb_app_DeviceWidth(),bb_app_DeviceHeight());
		}else{
			gl.viewport(t_cam.m_vx,t_cam.m_vy,t_cam.m_vwidth,t_cam.m_vheight);
		}
		gl.enable(3089);
		gl.scissor(t_cam.m_viewport[0],t_cam.m_viewport[1],t_cam.m_viewport[2],t_cam.m_viewport[3]);
	}
	gl.clearColor(t_cam.m_cls_r,t_cam.m_cls_g,t_cam.m_cls_b,1.0);
	if(t_cam.m_cls_color==true && t_cam.m_cls_zbuffer==true){
		gl.depthMask(true);
		gl.clear(16640);
	}else{
		if(t_cam.m_cls_color==true){
			gl.clear(16384);
		}else{
			if(t_cam.m_cls_zbuffer==true){
				gl.depthMask(true);
				gl.clear(256);
			}
		}
	}
	this.m_vmat_array=t_cam.m_mod_mat.p_ToArray();
	this.m_pvmat_array=t_cam.m_projview_mat.p_ToArray();
	if(false && ((c_OpenglES20.m_GetGLError())!=0)){
		print("*cam err");
	}
	return 0;
}
c_OpenglES20.prototype.p_SetShader2D=function(){
	c_TShader.m_SetShader(c_FullShader.m_fastbrightshader);
}
function c_BlankShader(){
	Object.call(this);
	this.implments={c_IShader2D:1};
}
c_BlankShader.m_new=function(){
	return this;
}
c_BlankShader.prototype.p_SetShader2D=function(){
}
function c_MojoEmulationDevice(){
	gxtkGraphics.call(this);
	this.m_mesh=null;
	this.m_layer=0;
	this.m_solid=new_object_array(512);
	this.m_lastBlend=-1;
	this.m_lastSurface=null;
	this.m_mat=c_Matrix.m_new.call(new c_Matrix);
	this.m_fontImage=null;
	this.m_fontFile="mojo_font.png";
	this.m_colora=1.0;
	this.m_colorr=0;
	this.m_colorg=0;
	this.m_colorb=0;
	this.m_zdepth=1.99999;
}
c_MojoEmulationDevice.prototype=extend_class(gxtkGraphics);
c_MojoEmulationDevice.m__device=null;
c_MojoEmulationDevice.m__olddevice=null;
c_MojoEmulationDevice.m_new=function(){
	return this;
}
c_MojoEmulationDevice.m__quadCache=null;
c_MojoEmulationDevice.prototype.p_NewLayer=function(){
	if(this.m_mesh==null){
		return;
	}
	this.m_layer+=1;
	if(this.m_layer>512){
		this.m_layer=0;
	}
	if(!((this.m_solid[this.m_layer])!=null)){
		this.m_solid[this.m_layer]=this.m_mesh.p_CreateSurface(null);
	}else{
		this.m_mesh.p_AddSurface(this.m_solid[this.m_layer]);
	}
	this.m_solid[this.m_layer].m_no_verts=0;
	this.m_solid[this.m_layer].m_no_tris=0;
	this.m_solid[this.m_layer].m_vbo_dyn=true;
	this.m_solid[this.m_layer].m_brush.m_fx=35;
	this.m_mesh.m_brush.m_blend=0;
}
c_MojoEmulationDevice.prototype.p_Reset=function(){
	if(!((this.m_mesh)!=null)){
		this.m_mesh=bb_functions_CreateMesh(null);
		this.m_mesh.m_entity_link.p_Remove();
		this.m_mesh.p_EntityFX(67);
		this.m_layer=-1;
		this.p_NewLayer();
	}
	this.m_mesh.m_no_surfs=1;
	this.m_mesh.m_surf_list.p_Clear();
	this.m_mesh.m_surf_list.p_AddLast2(this.m_solid[0]);
	this.m_solid[0].m_no_verts=0;
	this.m_solid[0].m_no_tris=0;
	this.m_layer=0;
	this.m_lastBlend=0;
	this.m_lastSurface=null;
	c_TRender.m_draw_list.p_AddLast3(this.m_mesh);
	this.m_mat.p_LoadIdentity();
	return 0;
}
c_MojoEmulationDevice.m_firstTimeRun=false;
c_MojoEmulationDevice.prototype.p_InitFont=function(){
	if(((this.m_fontImage)!=null) && this.m_fontImage.p_Width()>0){
		return;
	}
	this.m_fontImage=bb_graphics_LoadImage(this.m_fontFile,96,2);
	if(((this.m_fontImage)!=null) && this.m_fontImage.p_Width()>0){
		bb_graphics_SetFont(this.m_fontImage,32);
	}
}
c_MojoEmulationDevice.m_SetDevice=function(){
	if(!((c_MojoEmulationDevice.m__device)!=null)){
		c_MojoEmulationDevice.m__olddevice=bb_graphics_GetGraphicsDevice();
		c_MojoEmulationDevice.m__device=c_MojoEmulationDevice.m_new.call(new c_MojoEmulationDevice);
		c_MojoEmulationDevice.m__quadCache=c_QuadCache.m_new.call(new c_QuadCache);
	}
	c_MojoEmulationDevice.m__device.p_Reset();
	bb_graphics_SetGraphicsDevice(c_MojoEmulationDevice.m__device);
	bb_graphics_BeginRender();
	if(c_MojoEmulationDevice.m_firstTimeRun){
		c_MojoEmulationDevice.m__device.p_InitFont();
	}else{
		bb_functions_PreLoadPixmap2(c_MojoEmulationDevice.m__device.m_fontFile);
	}
	c_MojoEmulationDevice.m_firstTimeRun=true;
}
c_MojoEmulationDevice.prototype.Width=function(){
	return c_TRender.m_width;
}
c_MojoEmulationDevice.prototype.Height=function(){
	return c_TRender.m_height;
}
c_MojoEmulationDevice.prototype.LoadSurface=function(t_path){
	var t_msurf=c_MojoSurface.m_PreLoad(t_path,this.m_mesh,c_MojoEmulationDevice.m__device);
	return (t_msurf);
}
c_MojoEmulationDevice.prototype.CreateSurface=function(t_width,t_height){
	var t_msurf=c_MojoSurface.m_Create("");
	return (t_msurf);
}
c_MojoEmulationDevice.prototype.WritePixels2=function(t_surface,t_pixels,t_x,t_y,t_width,t_height,t_offset,t_pitch){
	return 0;
}
c_MojoEmulationDevice.prototype.Cls=function(t_r,t_g,t_b){
	c_TRender.m_camera2D.p_CameraClsColor(t_r,t_g,t_b);
	c_TRender.m_camera2D.p_CameraClsMode(true,false);
	return 0;
}
c_MojoEmulationDevice.prototype.SetAlpha=function(t_alpha){
	this.m_colora=t_alpha;
	return 0;
}
c_MojoEmulationDevice.prototype.SetColor=function(t_r,t_g,t_b){
	this.m_colorr=((t_r)|0);
	this.m_colorg=((t_g)|0);
	this.m_colorb=((t_b)|0);
	return 0;
}
c_MojoEmulationDevice.prototype.SetMatrix=function(t_ix,t_iy,t_jx,t_jy,t_tx,t_ty){
	this.m_mat.m_grid[0][0]=t_ix;
	this.m_mat.m_grid[1][0]=t_iy;
	this.m_mat.m_grid[0][1]=t_jx;
	this.m_mat.m_grid[1][1]=t_jy;
	this.m_mat.m_grid[3][0]=t_tx;
	this.m_mat.m_grid[3][1]=-t_ty;
	return 0;
}
c_MojoEmulationDevice.prototype.SetScissor=function(t_x,t_y,t_width,t_height){
	c_TRender.m_camera2D.p_CameraScissor(t_x,t_y,t_width,t_height);
	return 0;
}
c_MojoEmulationDevice.prototype.SetBlend=function(t_blend){
	if(t_blend!=this.m_lastBlend){
		this.p_NewLayer();
	}
	if(t_blend==1){
		this.m_solid[this.m_layer].m_brush.m_blend=3;
	}else{
		if(t_blend==1){
			this.m_solid[this.m_layer].m_brush.m_blend=4;
		}else{
			this.m_solid[this.m_layer].m_brush.m_blend=1;
			this.m_solid[this.m_layer].m_brush.m_blend=0;
		}
	}
	this.m_lastBlend=t_blend;
	return 0;
}
c_MojoEmulationDevice.prototype.p_Check=function(t_s){
	if(t_s!=this.m_lastSurface){
		this.p_NewLayer();
		this.m_lastSurface=t_s;
	}
	return 0;
}
c_MojoEmulationDevice.prototype.p_AddQuad=function(t_s,t_x,t_y,t_w,t_h,t_u,t_v,t_uw,t_vh){
	var t_p0=[];
	var t_p1=[];
	var t_p2=[];
	var t_p3=[];
	var t_r=.0;
	var t_g=.0;
	var t_b=.0;
	t_p0=bb_mojographics_Transform2D(this.m_mat,t_x,-t_h-t_y,this.m_zdepth);
	t_p1=bb_mojographics_Transform2D(this.m_mat,t_x,-t_y,this.m_zdepth);
	t_p2=bb_mojographics_Transform2D(this.m_mat,t_x+t_w,-t_y,this.m_zdepth);
	t_p3=bb_mojographics_Transform2D(this.m_mat,t_x+t_w,-t_h-t_y,this.m_zdepth);
	t_r=(this.m_colorr)*0.0039215686274509803;
	t_g=(this.m_colorg)*0.0039215686274509803;
	t_b=(this.m_colorb)*0.0039215686274509803;
	c_MojoEmulationDevice.m__quadCache.p_AddCache(t_s,[t_p0[0],t_p0[1],-t_p0[2],0.0,1.0,1.0,1.0,0.0,t_r,t_g,t_b,this.m_colora,t_u,t_vh,t_u,t_vh,t_p1[0],t_p1[1],-t_p1[2],0.0,1.0,1.0,1.0,0.0,t_r,t_g,t_b,this.m_colora,t_u,t_v,t_u,t_v,t_p2[0],t_p2[1],-t_p2[2],0.0,1.0,1.0,1.0,0.0,t_r,t_g,t_b,this.m_colora,t_uw,t_v,t_uw,t_v,t_p3[0],t_p3[1],-t_p3[2],0.0,1.0,1.0,1.0,0.0,t_r,t_g,t_b,this.m_colora,t_uw,t_vh,t_uw,t_vh]);
	t_s.m_reset_vbo=-1;
}
c_MojoEmulationDevice.prototype.DrawPoint=function(t_x,t_y){
	this.p_Check(null);
	this.p_AddQuad(this.m_solid[this.m_layer],t_x,t_y,1.0,1.0,0.0,0.0,1.0,1.0);
	return 0;
}
c_MojoEmulationDevice.prototype.DrawRect=function(t_x,t_y,t_w,t_h){
	this.p_Check(null);
	this.p_AddQuad(this.m_solid[this.m_layer],t_x,t_y,t_w,t_h,0.0,0.0,1.0,1.0);
	return 0;
}
c_MojoEmulationDevice.prototype.DrawLine=function(t_x1,t_y1,t_x2,t_y2){
	this.p_Check(null);
	var t_p0=[];
	t_p0=bb_mojographics_Transform2D(this.m_mat,t_x1,-t_y1,this.m_zdepth);
	t_x1=t_p0[0];
	t_y1=t_p0[1];
	t_p0=bb_mojographics_Transform2D(this.m_mat,t_x2,-t_y2,this.m_zdepth);
	t_x2=t_p0[0];
	t_y2=t_p0[1];
	var t_px=t_y1-t_y2;
	var t_py=-(t_x1-t_x2);
	var t_d=1.0/Math.sqrt(t_px*t_px+t_py*t_py);
	t_px=t_px*t_d;
	t_py=t_py*t_d;
	var t_v0=this.m_solid[this.m_layer].p_AddVertex(t_x2,t_y2,this.m_zdepth,0.0,1.0,0.0);
	var t_v1=this.m_solid[this.m_layer].p_AddVertex(t_x1,t_y1,this.m_zdepth,0.0,0.0,0.0);
	var t_v2=this.m_solid[this.m_layer].p_AddVertex(t_x1+t_px,t_y1+t_py,this.m_zdepth,1.0,0.0,0.0);
	this.m_solid[this.m_layer].p_AddTriangle(t_v0,t_v1,t_v2);
	var t_v3=this.m_solid[this.m_layer].p_AddVertex(t_x2+t_px,t_y2+t_py,this.m_zdepth,1.0,0.0,0.0);
	this.m_solid[this.m_layer].p_AddTriangle(t_v0,t_v2,t_v3);
	this.m_solid[this.m_layer].p_VertexColor(t_v0,(this.m_colorr),(this.m_colorg),(this.m_colorb),this.m_colora);
	this.m_solid[this.m_layer].p_VertexColor(t_v1,(this.m_colorr),(this.m_colorg),(this.m_colorb),this.m_colora);
	this.m_solid[this.m_layer].p_VertexColor(t_v2,(this.m_colorr),(this.m_colorg),(this.m_colorb),this.m_colora);
	this.m_solid[this.m_layer].p_VertexColor(t_v3,(this.m_colorr),(this.m_colorg),(this.m_colorb),this.m_colora);
	this.m_solid[this.m_layer].m_reset_vbo=-1;
	return 0;
}
c_MojoEmulationDevice.prototype.DrawPoly=function(t_verts){
	this.p_Check(null);
	var t_p0=[];
	var t_p1=[];
	var t_p2=[];
	var t_p3=[];
	var t_r=.0;
	var t_g=.0;
	var t_b=.0;
	var t_tris=new_number_array((t_verts.length-7)*6*3);
	var t_t=0;
	var t_vt=this.m_solid[this.m_layer].m_no_verts;
	t_r=(this.m_colorr)*0.0039215686274509803;
	t_g=(this.m_colorg)*0.0039215686274509803;
	t_b=(this.m_colorb)*0.0039215686274509803;
	for(var t_i=0;t_i<=t_verts.length-7;t_i=t_i+6){
		t_p0=bb_mojographics_Transform2D(this.m_mat,t_verts[t_i+0],-t_verts[t_i+1],this.m_zdepth);
		t_p1=bb_mojographics_Transform2D(this.m_mat,t_verts[t_i+2],-t_verts[t_i+3],this.m_zdepth);
		t_p2=bb_mojographics_Transform2D(this.m_mat,t_verts[t_i+4],-t_verts[t_i+5],this.m_zdepth);
		t_vt=t_vt+3;
		this.m_solid[this.m_layer].p_AddVertex2([t_p0[0],t_p0[1],-t_p0[2],0.0,1.0,1.0,1.0,0.0,(this.m_colorr)*0.0039215686274509803,(this.m_colorg)*0.0039215686274509803,(this.m_colorb)*0.0039215686274509803,this.m_colora,0.0,0.0,0.0,0.0,t_p1[0],t_p1[1],-t_p1[2],0.0,1.0,1.0,1.0,0.0,(this.m_colorr)*0.0039215686274509803,(this.m_colorg)*0.0039215686274509803,(this.m_colorb)*0.0039215686274509803,this.m_colora,0.0,0.0,0.0,0.0,t_p2[0],t_p2[1],-t_p2[2],0.0,1.0,1.0,1.0,0.0,(this.m_colorr)*0.0039215686274509803,(this.m_colorg)*0.0039215686274509803,(this.m_colorb)*0.0039215686274509803,this.m_colora,0.0,0.0,0.0,0.0],-1);
		t_tris[t_t]=t_vt-3;
		t_tris[t_t+1]=t_vt-2;
		t_tris[t_t+2]=t_vt-1;
		t_t=t_t+3;
	}
	this.m_solid[this.m_layer].p_AddTriangle2(t_tris,-1);
	return 0;
}
c_MojoEmulationDevice.prototype.DrawOval=function(t_x,t_y,t_w,t_h){
	this.p_Check(null);
	if(t_w<0.0){
		t_w=-t_w;
	}
	if(t_h<0.0){
		t_h=-t_h;
	}
	var t_seg=(((t_w+t_h)*0.2)|0);
	if(t_seg<14){
		t_seg=14;
	}
	var t_deg=0.0;
	var t_deginc=360.0/(t_seg);
	var t_verts=new_number_array(t_seg*6+1);
	for(var t_i=0;t_i<=t_verts.length-7;t_i=t_i+6){
		t_verts[t_i+0]=t_x;
		t_verts[t_i+1]=t_y;
		t_verts[t_i+2]=t_x+t_w*Math.cos((t_deg)*D2R);
		t_verts[t_i+3]=t_y+t_h*Math.sin((t_deg)*D2R);
		t_verts[t_i+4]=t_x+t_w*Math.cos((t_deg+t_deginc)*D2R);
		t_verts[t_i+5]=t_y+t_h*Math.sin((t_deg+t_deginc)*D2R);
		t_deg+=t_deginc;
	}
	this.DrawPoly(t_verts);
	return 0;
}
c_MojoEmulationDevice.prototype.DrawSurface2=function(t_surface,t_x,t_y,t_srcx,t_srcy,t_srcw,t_srch){
	var t_s=object_downcast((t_surface),c_MojoSurface);
	if(!((t_s)!=null)){
		return 0;
	}
	this.p_Check(t_s);
	if((t_s.m_tex)!=null){
		this.m_solid[this.m_layer].p_PaintSurface2(t_s.m_tex,0);
	}
	var t_xstep=t_s.m_xstep;
	var t_ystep=t_s.m_ystep;
	var t_upos=(t_srcx)*t_xstep;
	var t_vpos=(t_srcy)*t_ystep;
	this.p_AddQuad(this.m_solid[this.m_layer],t_x,t_y,(t_srcw),(t_srch),t_upos,t_vpos,t_upos+t_xstep*(t_srcw),t_vpos+t_ystep*(t_srch));
	return 0;
}
c_MojoEmulationDevice.prototype.DrawSurface=function(t_surface,t_x,t_y){
	this.DrawSurface2(t_surface,t_x,t_y,0,0,t_surface.Width(),t_surface.Height());
	return 0;
}
c_MojoEmulationDevice.prototype.ReadPixels=function(t_pixels,t_x,t_y,t_width,t_height,t_offset,t_pitch){
	return 0;
}
function bb_graphics_GetGraphicsDevice(){
	return bb_graphics_device;
}
function c_QuadCache(){
	Object.call(this);
	this.m_q_num=0;
	this.m_q_cache=new_number_array(1280);
	this.m_q_surf=null;
	this.m_q_tri=new_number_array(128);
}
c_QuadCache.m_new=function(){
	return this;
}
c_QuadCache.prototype.p_FlushCache=function(){
	if(this.m_q_num==0){
		return;
	}
	var t_n=this.m_q_num;
	var t_v4=this.m_q_surf.p_AddVertex2(this.m_q_cache,this.m_q_num*64);
	t_v4=t_v4+1-this.m_q_num*4;
	var t_k=0;
	for(var t_j=0;t_j<=t_n-1;t_j=t_j+1){
		this.m_q_tri[t_k+0]=t_v4;
		this.m_q_tri[t_k+1]=t_v4+1;
		this.m_q_tri[t_k+2]=t_v4+2;
		this.m_q_tri[t_k+3]=t_v4;
		this.m_q_tri[t_k+4]=t_v4+2;
		this.m_q_tri[t_k+5]=t_v4+3;
		t_k=t_k+6;
		t_v4=t_v4+4;
	}
	this.m_q_surf.p_AddTriangle2(this.m_q_tri,this.m_q_num*6);
	this.m_q_num=0;
	this.m_q_surf=null;
}
c_QuadCache.prototype.p_AddCache=function(t_s,t_v){
	if(t_s!=this.m_q_surf || this.m_q_num>19){
		this.p_FlushCache();
	}
	this.m_q_surf=t_s;
	var t_offset=this.m_q_num*64;
	for(var t_i=0;t_i<=63;t_i=t_i+1){
		this.m_q_cache[t_offset+t_i]=t_v[t_i];
	}
	this.m_q_num+=1;
}
function c_TEntity(){
	Object.call(this);
	this.m_mat=c_Matrix.m_new.call(new c_Matrix);
	this.m_loc_mat=c_Matrix.m_new.call(new c_Matrix);
	this.m_classname="";
	this.m_parent=null;
	this.m_px=.0;
	this.m_py=.0;
	this.m_pz=.0;
	this.m_rx=.0;
	this.m_ry=.0;
	this.m_rz=.0;
	this.m_sx=1.0;
	this.m_sy=1.0;
	this.m_sz=1.0;
	this.m_gsx=1.0;
	this.m_gsy=1.0;
	this.m_gsz=1.0;
	this.m_collision=c_TCollision.m_new.call(new c_TCollision);
	this.m_child_list=c_EntityList.m_new.call(new c_EntityList);
	this.m_parent_link=null;
	this.m_order=0;
	this.m_entity_link=null;
	this.m_brush=c_TBrush.m_new.call(new c_TBrush);
	this.m_name="";
	this.m_anim=0;
	this.m_hide=false;
	this.m_cull_radius=.0;
	this.m_anim_update=0;
	this.m_anim_seqs_first=new_number_array(1);
	this.m_anim_seq=0;
	this.m_anim_seqs_last=new_number_array(1);
	this.m_anim_trans=0;
	this.m_anim_time=.0;
	this.m_anim_render=0;
	this.m_anim_mode=0;
	this.m_anim_speed=.0;
	this.m_anim_dir=1;
	this.m_use_cam_layer=false;
	this.m_cam_layer=null;
	this.m_auto_fade=0;
	this.m_fade_near=.0;
	this.m_fade_far=.0;
	this.m_fade_alpha=.0;
	this.m_using_alpha=false;
	this.m_alpha_order=.0;
	this.m_shader_brush=null;
}
c_TEntity.m_new=function(){
	this.m_mat.p_LoadIdentity();
	this.m_loc_mat.p_LoadIdentity();
	return this;
}
c_TEntity.prototype.p_UpdateMat=function(t_load_identity){
	if(t_load_identity==true){
		this.m_mat.p_LoadIdentity();
	}
	if(t_load_identity==false && ((this.m_parent)!=null)){
		this.m_mat.p_Overwrite(this.m_parent.m_mat);
	}
	this.m_mat.p_Translate(this.m_px,this.m_py,this.m_pz);
	this.m_mat.p_Rotate(this.m_rx,this.m_ry,this.m_rz);
	this.m_mat.p_Scale(this.m_sx,this.m_sy,this.m_sz);
	if(t_load_identity){
		this.m_loc_mat.p_Overwrite(this.m_mat);
	}
	if((this.m_parent)!=null){
		this.m_gsx=this.m_parent.m_gsx*this.m_sx;
		this.m_gsy=this.m_parent.m_gsy*this.m_sy;
		this.m_gsz=this.m_parent.m_gsz*this.m_sz;
	}else{
		this.m_gsx=this.m_sx;
		this.m_gsy=this.m_sy;
		this.m_gsz=this.m_sz;
	}
	this.m_collision.m_updated_shape=false;
}
c_TEntity.prototype.p_AddParent=function(t_parent_ent){
	this.m_parent=t_parent_ent;
	if(this.m_parent!=null){
		this.m_mat.p_Overwrite(this.m_parent.m_mat);
		this.p_UpdateMat(false);
		this.m_parent_link=this.m_parent.m_child_list.p_AddLast(this);
	}
	return 0;
}
c_TEntity.m_entity_list=null;
c_TEntity.prototype.p_EntityFX=function(t_fx_no){
	this.m_brush.m_fx=t_fx_no;
	return this;
}
c_TEntity.prototype.p_EntityFX2=function(){
	return this.m_brush.m_fx;
}
c_TEntity.prototype.p_X=function(){
	return this.m_mat.m_grid[3][0];
}
c_TEntity.prototype.p_X2=function(t_xx){
	this.m_mat.m_grid[3][0]=t_xx;
}
c_TEntity.prototype.p_Y=function(){
	return this.m_mat.m_grid[3][1];
}
c_TEntity.prototype.p_Y2=function(t_yy){
	this.m_mat.m_grid[3][1]=t_yy;
}
c_TEntity.prototype.p_Z=function(){
	return -this.m_mat.m_grid[3][2];
}
c_TEntity.prototype.p_Z2=function(t_zz){
	this.m_mat.m_grid[3][2]=-t_zz;
}
c_TEntity.m_temp_mat=null;
c_TEntity.prototype.p_UpdateMatTrans=function(t_load_identity){
	if(t_load_identity==false && ((this.m_parent)!=null)){
		this.m_mat.m_grid[3][0]=this.m_parent.m_mat.m_grid[0][0]*this.m_px+this.m_parent.m_mat.m_grid[1][0]*this.m_py+this.m_parent.m_mat.m_grid[2][0]*this.m_pz+this.m_parent.m_mat.m_grid[3][0];
		this.m_mat.m_grid[3][1]=this.m_parent.m_mat.m_grid[0][1]*this.m_px+this.m_parent.m_mat.m_grid[1][1]*this.m_py+this.m_parent.m_mat.m_grid[2][1]*this.m_pz+this.m_parent.m_mat.m_grid[3][1];
		this.m_mat.m_grid[3][2]=this.m_parent.m_mat.m_grid[0][2]*this.m_px+this.m_parent.m_mat.m_grid[1][2]*this.m_py+this.m_parent.m_mat.m_grid[2][2]*this.m_pz+this.m_parent.m_mat.m_grid[3][2];
	}else{
		this.m_mat.m_grid[3][0]=this.m_px;
		this.m_mat.m_grid[3][1]=this.m_py;
		this.m_mat.m_grid[3][2]=this.m_pz;
	}
	if(t_load_identity){
		this.m_loc_mat.p_Overwrite(this.m_mat);
	}
	return 0;
}
c_TEntity.m_UpdateChildren=function(t_ent_p,t_type){
	var t_=t_ent_p.m_child_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_ent_c=t_.p_NextObject();
		if(object_downcast((t_ent_c),c_TBone)==null){
			if(t_type==0){
				t_ent_c.m_mat.p_Overwrite(t_ent_p.m_mat);
				t_ent_c.p_UpdateMat(false);
			}else{
				if(t_type==1){
					t_ent_c.p_UpdateMatTrans(false);
				}else{
					t_ent_c.m_mat.p_Overwrite(t_ent_p.m_mat);
					t_ent_c.p_UpdateMat(false);
				}
			}
			c_TEntity.m_UpdateChildren(t_ent_c,t_type);
		}else{
			object_downcast((t_ent_c),c_TBone).p_UpdateMatrix(t_ent_c.m_loc_mat);
		}
	}
	return 0;
}
c_TEntity.prototype.p_PositionEntity=function(t_x,t_y,t_z,t_glob){
	t_z=-t_z;
	if(t_glob==1 && this.m_parent!=null){
		c_TEntity.m_temp_mat=this.m_parent.m_mat.p_Copy().p_Inverse();
		var t_psx=this.m_parent.m_gsx;
		var t_psy=this.m_parent.m_gsy;
		var t_psz=this.m_parent.m_gsz;
		var t_pos=c_TEntity.m_temp_mat.p_TransformPoint(t_x,t_y,t_z,1.0);
		t_x=t_pos[0]/(t_psx*t_psx);
		t_y=t_pos[1]/(t_psy*t_psy);
		t_z=t_pos[2]/(t_psz*t_psz);
	}
	if(object_downcast((this),c_TBone)!=null){
		object_downcast((this),c_TBone).p_PositionBone(t_x,t_y,t_z,t_glob);
		return this;
	}
	this.m_px=t_x;
	this.m_py=t_y;
	this.m_pz=t_z;
	if(this.m_parent!=null){
		this.p_UpdateMatTrans(false);
	}else{
		this.p_UpdateMatTrans(true);
	}
	if(this.m_child_list.p_IsEmpty()!=true){
		c_TEntity.m_UpdateChildren(this,1);
	}
	return this;
}
c_TEntity.prototype.p_PositionEntity2=function(t_e){
	this.p_PositionEntity(t_e.p_X(),t_e.p_Y(),t_e.p_Z(),1);
	return this;
}
c_TEntity.prototype.p_EntityShininess=function(t_s){
	this.m_brush.m_shine=t_s;
	return this;
}
c_TEntity.prototype.p_EntityTexture=function(t_texture,t_frame,t_index){
	this.m_brush.m_tex[t_index]=t_texture;
	if(t_index+1>this.m_brush.m_no_texs){
		this.m_brush.m_no_texs=t_index+1;
	}
	if(t_frame<0){
		t_frame=0;
	}
	if(t_frame>t_texture.m_no_frames-1){
		t_frame=t_texture.m_no_frames-1;
	}
	this.m_brush.m_tex[t_index].m_tex_frame=t_frame;
	if(t_frame>0 && t_texture.m_no_frames>1){
		var t_x=t_frame % t_texture.m_frame_xstep;
		var t_y=((t_frame/t_texture.m_frame_ystep)|0) % t_texture.m_frame_ystep;
		this.m_brush.m_tex[t_index].m_u_pos=(t_x)*t_texture.m_frame_ustep;
		this.m_brush.m_tex[t_index].m_v_pos=(t_y)*t_texture.m_frame_vstep;
	}
	return this;
}
c_TEntity.prototype.p_HideEntity=function(){
	this.m_hide=true;
	var t_=this.m_child_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_ent=t_.p_NextObject();
		t_ent.p_HideEntity();
	}
	return this;
}
c_TEntity.prototype.p_MoveEntity=function(t_mx,t_my,t_mz){
	t_mz=-t_mz;
	var t_n=[];
	t_n=this.m_mat.p_TransformPoint(t_mx/this.m_gsx,t_my/this.m_gsy,t_mz/this.m_gsz,1.0);
	this.p_PositionEntity(t_n[0],t_n[1],-t_n[2],1);
	return this;
}
c_TEntity.prototype.p_UpdateMatRot=function(t_load_identity){
	if(t_load_identity==false && ((this.m_parent)!=null)){
		this.m_mat.m_grid[0][0]=this.m_parent.m_mat.m_grid[0][0];
		this.m_mat.m_grid[0][1]=this.m_parent.m_mat.m_grid[0][1];
		this.m_mat.m_grid[0][2]=this.m_parent.m_mat.m_grid[0][2];
		this.m_mat.m_grid[1][0]=this.m_parent.m_mat.m_grid[1][0];
		this.m_mat.m_grid[1][1]=this.m_parent.m_mat.m_grid[1][1];
		this.m_mat.m_grid[1][2]=this.m_parent.m_mat.m_grid[1][2];
		this.m_mat.m_grid[2][0]=this.m_parent.m_mat.m_grid[2][0];
		this.m_mat.m_grid[2][1]=this.m_parent.m_mat.m_grid[2][1];
		this.m_mat.m_grid[2][2]=this.m_parent.m_mat.m_grid[2][2];
		this.m_mat.p_Rotate(this.m_rx,this.m_ry,this.m_rz);
		this.m_mat.p_Scale(this.m_sx,this.m_sy,this.m_sz);
	}else{
		this.m_mat.p_FastRotateScale(this.m_rx,this.m_ry,this.m_rz,this.m_sx,this.m_sy,this.m_sz);
	}
	if((this.m_parent)!=null){
		this.m_gsx=this.m_parent.m_gsx*this.m_sx;
		this.m_gsy=this.m_parent.m_gsy*this.m_sy;
		this.m_gsz=this.m_parent.m_gsz*this.m_sz;
	}else{
		this.m_gsx=this.m_sx;
		this.m_gsy=this.m_sy;
		this.m_gsz=this.m_sz;
	}
	if(t_load_identity){
		this.m_loc_mat.p_Overwrite(this.m_mat);
	}
	return 0;
}
c_TEntity.prototype.p_TurnEntity=function(t_x,t_y,t_z,t_glob){
	if(t_glob==1 && this.m_parent!=null){
	}
	this.m_rx=this.m_rx+-t_x;
	this.m_ry=this.m_ry+t_y;
	this.m_rz=this.m_rz+t_z;
	if(object_downcast((this),c_TBone)!=null){
		object_downcast((this),c_TBone).p_RotateBone(this.m_rx,this.m_ry,this.m_rz,t_glob);
		return this;
	}
	if(this.m_parent!=null){
		this.p_UpdateMatRot(false);
	}else{
		this.p_UpdateMatRot(true);
	}
	if(this.m_child_list.p_IsEmpty()!=true){
		c_TEntity.m_UpdateChildren(this,0);
	}
	return this;
}
c_TEntity.prototype.p_EntityX=function(t_glob){
	if(t_glob==0){
		return this.m_px;
	}else{
		return this.m_mat.m_grid[3][0];
	}
}
c_TEntity.prototype.p_EntityY=function(t_glob){
	if(t_glob==0){
		return this.m_py;
	}else{
		return this.m_mat.m_grid[3][1];
	}
}
c_TEntity.prototype.p_EntityZ=function(t_glob){
	if(t_glob==0){
		return -this.m_pz;
	}else{
		return -this.m_mat.m_grid[3][2];
	}
}
c_TEntity.prototype.p_EntityPitch=function(t_glob){
	if(t_glob==0){
		return -this.m_rx;
	}else{
		var t_ang=(Math.atan2(this.m_mat.m_grid[2][1],Math.sqrt(this.m_mat.m_grid[2][0]*this.m_mat.m_grid[2][0]+this.m_mat.m_grid[2][2]*this.m_mat.m_grid[2][2]))*R2D);
		if(t_ang<=0.0001 && t_ang>=-0.0001){
			t_ang=0.0;
		}
		return t_ang;
	}
}
c_TEntity.prototype.p_EntityYaw=function(t_glob){
	if(t_glob==0){
		return this.m_ry;
	}else{
		var t_a=this.m_mat.m_grid[2][0];
		var t_b=this.m_mat.m_grid[2][2];
		if(t_a<=0.0001 && t_a>=-0.0001){
			t_a=0.0;
		}
		if(t_b<=0.0001 && t_b>=-0.0001){
			t_b=0.0;
		}
		return (Math.atan2(t_a,t_b)*R2D);
	}
}
c_TEntity.prototype.p_EntityRoll=function(t_glob){
	if(t_glob==0){
		return this.m_rz;
	}else{
		var t_a=this.m_mat.m_grid[0][1];
		var t_b=this.m_mat.m_grid[1][1];
		if(t_a<=0.0001 && t_a>=-0.0001){
			t_a=0.0;
		}
		if(t_b<=0.0001 && t_b>=-0.0001){
			t_b=0.0;
		}
		return (Math.atan2(t_a,t_b)*R2D);
	}
}
c_TEntity.prototype.p_RotateEntity=function(t_x,t_y,t_z,t_glob){
	this.m_rx=-t_x;
	this.m_ry=t_y;
	this.m_rz=t_z;
	if(t_glob==1 && this.m_parent!=null){
		this.m_rx=this.m_rx+this.m_parent.p_EntityPitch(1);
		this.m_ry=this.m_ry-this.m_parent.p_EntityYaw(1);
		this.m_rz=this.m_rz-this.m_parent.p_EntityRoll(1);
	}
	if(object_downcast((this),c_TBone)!=null){
		object_downcast((this),c_TBone).p_RotateBone(this.m_rx,this.m_ry,this.m_rz,t_glob);
		return this;
	}
	if(this.m_parent!=null){
		this.p_UpdateMatRot(false);
	}else{
		this.p_UpdateMatRot(true);
	}
	if(this.m_child_list.p_IsEmpty()!=true){
		c_TEntity.m_UpdateChildren(this,0);
	}
	return this;
}
c_TEntity.prototype.p_RotateEntity2=function(t_e){
	this.p_RotateEntity(t_e.m_rx,t_e.m_ry,t_e.m_rz,1);
	return this;
}
c_TEntity.prototype.p_EntityScaleX=function(t_glob){
	if(t_glob==1){
		if(this.m_parent!=null){
			var t_ent=this;
			var t_x=this.m_sx;
			do{
				t_x=t_x*t_ent.m_parent.m_sx;
				t_ent=t_ent.m_parent;
			}while(!(t_ent.m_parent==null));
			return t_x;
		}
	}
	return this.m_sx;
}
c_TEntity.prototype.p_EntityScaleY=function(t_glob){
	if(t_glob==1){
		if(this.m_parent!=null){
			var t_ent=this;
			var t_y=this.m_sy;
			do{
				t_y=t_y*t_ent.m_parent.m_sy;
				t_ent=t_ent.m_parent;
			}while(!(t_ent.m_parent==null));
			return t_y;
		}
	}
	return this.m_sy;
}
c_TEntity.prototype.p_EntityScaleZ=function(t_glob){
	if(t_glob==1){
		if(this.m_parent!=null){
			var t_ent=this;
			var t_z=this.m_sz;
			do{
				t_z=t_z*t_ent.m_parent.m_sz;
				t_ent=t_ent.m_parent;
			}while(!(t_ent.m_parent==null));
			return t_z;
		}
	}
	return this.m_sz;
}
c_TEntity.prototype.p_ScaleEntity=function(t_x,t_y,t_z,t_glob){
	this.m_sx=t_x;
	this.m_sy=t_y;
	this.m_sz=t_z;
	if(t_glob==1 && this.m_parent!=null){
		this.m_sx=this.m_sx/this.m_parent.m_gsx;
		this.m_sy=this.m_sy/this.m_parent.m_gsy;
		this.m_sz=this.m_sz/this.m_parent.m_gsz;
	}
	if(object_downcast((this),c_TBone)!=null){
		object_downcast((this),c_TBone).p_ScaleBone(this.m_sx,this.m_sy,this.m_sz,t_glob);
		return null;
	}
	if(this.m_parent!=null){
		this.p_UpdateMatRot(false);
	}else{
		this.p_UpdateMatRot(true);
	}
	if(this.m_child_list.p_IsEmpty()!=true){
		c_TEntity.m_UpdateChildren(this,0);
	}
	return this;
}
c_TEntity.prototype.p_ScaleEntity2=function(t_e){
	this.p_ScaleEntity(t_e.p_EntityScaleX(0),t_e.p_EntityScaleY(0),t_e.p_EntityScaleZ(0),1);
	return this;
}
c_TEntity.prototype.p_EntityParent=function(t_parent_ent,t_glob){
	var t_gpx_=this.p_EntityX(1);
	var t_gpy_=this.p_EntityY(1);
	var t_gpz_=this.p_EntityZ(1);
	var t_grx_=this.p_EntityPitch(1);
	var t_gry_=this.p_EntityYaw(1);
	var t_grz_=this.p_EntityRoll(1);
	if(this.m_parent!=null){
		this.m_parent_link.p_Remove();
		this.m_parent=null;
	}
	this.m_px=t_gpx_;
	this.m_py=t_gpy_;
	this.m_pz=-t_gpz_;
	this.m_rx=-t_grx_;
	this.m_ry=t_gry_;
	this.m_rz=t_grz_;
	if(t_parent_ent==null){
		this.p_UpdateMat(true);
		return null;
	}
	if(t_parent_ent!=null){
		if(t_glob){
			this.p_AddParent(t_parent_ent);
			this.p_PositionEntity(t_gpx_,t_gpy_,t_gpz_,1);
			this.p_RotateEntity(t_grx_,t_gry_,t_grz_,1);
			this.p_ScaleEntity(this.m_gsx,this.m_gsy,this.m_gsz,1);
		}else{
			this.p_AddParent(t_parent_ent);
		}
	}
	return this;
}
c_TEntity.prototype.p_Hidden=function(){
	return this.m_hide;
}
c_TEntity.prototype.p_EntityRadius=function(t_x,t_y){
	if(t_x==0.0){
		var t_m=object_downcast((this),c_TMesh);
		if((t_m)!=null){
			if(this.m_cull_radius==0.0){
				t_m.p_GetBounds(false);
			}
			var t_c=.0;
			t_c=bb_math_Max2(bb_math_Max2(bb_math_Abs2(t_m.m_max_x),bb_math_Abs2(t_m.m_max_y)),bb_math_Abs2(t_m.m_max_z));
			t_c=bb_math_Max2(bb_math_Max2(bb_math_Max2(t_c,bb_math_Abs2(t_m.m_min_x)),bb_math_Abs2(t_m.m_min_y)),bb_math_Abs2(t_m.m_min_z));
			t_x=t_c*1.4142135623;
			t_x=Math.sqrt(t_x*t_x+t_c*t_c);
			t_x=t_m.p_GetSphereBounds();
		}else{
			t_x=1.0;
		}
	}
	this.m_collision.m_radius_x=t_x;
	if(t_y==0.0){
		this.m_collision.m_radius_y=t_x;
	}else{
		this.m_collision.m_radius_y=t_y;
	}
	if(this.m_collision.m_box_w==0.0){
		this.m_collision.m_box_x=-this.m_collision.m_radius_x*0.5;
		this.m_collision.m_box_y=-this.m_collision.m_radius_x*0.5;
		this.m_collision.m_box_z=-this.m_collision.m_radius_x*0.5;
		this.m_collision.m_box_w=this.m_collision.m_radius_x;
		this.m_collision.m_box_h=this.m_collision.m_radius_x;
		this.m_collision.m_box_d=this.m_collision.m_radius_x;
	}
	this.m_collision.m_updated_shape=false;
	return this.m_collision.m_radius_x;
}
c_TEntity.prototype.p_EntityBox=function(t_x,t_y,t_z,t_w,t_h,t_d){
	if(t_x==0.0 && t_w==0.0 && t_d==0.0){
		if((object_downcast((this),c_TMesh))!=null){
			var t_m=object_downcast((this),c_TMesh);
			if(!((this.m_cull_radius)!=0.0)){
				t_m.p_GetBounds(false);
			}
			t_x=t_m.m_min_x;
			t_y=t_m.m_min_y;
			t_z=t_m.m_min_z;
			t_w=bb_math_Abs2(t_m.m_max_x-t_m.m_min_x);
			t_h=bb_math_Abs2(t_m.m_max_y-t_m.m_min_y);
			t_d=bb_math_Abs2(t_m.m_max_z-t_m.m_min_z);
		}
	}
	this.m_collision.m_box_x=t_x;
	this.m_collision.m_box_y=t_y;
	this.m_collision.m_box_z=t_z;
	this.m_collision.m_box_w=t_w;
	this.m_collision.m_box_h=t_h;
	this.m_collision.m_box_d=t_d;
	if(this.m_collision.m_radius_x==0.0){
		var t_c=.0;
		t_c=bb_math_Max2(bb_math_Max2(bb_math_Abs2(t_x),bb_math_Abs2(t_y)),bb_math_Abs2(t_z));
		t_c=bb_math_Max2(bb_math_Max2(bb_math_Max2(t_c,bb_math_Abs2(t_x+t_w)),bb_math_Abs2(t_y+t_h)),bb_math_Abs2(t_z+t_d));
		var t_xx=t_c*1.4142135623;
		this.m_collision.m_radius_x=Math.sqrt(t_xx*t_xx+t_c*t_c);
	}
	this.m_collision.m_updated_shape=false;
	return 0;
}
c_TEntity.prototype.p_EntityDistanceSquared=function(t_ent2){
	var t_xd=t_ent2.m_mat.m_grid[3][0]-this.m_mat.m_grid[3][0];
	var t_yd=t_ent2.m_mat.m_grid[3][1]-this.m_mat.m_grid[3][1];
	var t_zd=-t_ent2.m_mat.m_grid[3][2]+this.m_mat.m_grid[3][2];
	return t_xd*t_xd+t_yd*t_yd+t_zd*t_zd;
}
c_TEntity.prototype.p_EntityDistance=function(t_ent2){
	return Math.sqrt(this.p_EntityDistanceSquared(t_ent2));
}
c_TEntity.prototype.p_EntityName=function(){
	return this.m_name;
}
c_TEntity.m_global_mat=null;
function c_TMesh(){
	c_TEntity.call(this);
	this.m_surf_list=c_List2.m_new.call(new c_List2);
	this.m_no_surfs=0;
	this.m_anim_surf=[];
	this.m_anim_surf_frame=[];
	this.m_reset_bounds=1;
	this.m_col_tree=c_TColTree.m_new.call(new c_TColTree);
	this.m_is_sprite=false;
	this.m_total_tris=0;
	this.m_min_x=.0;
	this.m_max_x=.0;
	this.m_min_y=.0;
	this.m_max_y=.0;
	this.m_min_z=.0;
	this.m_max_z=.0;
	this.m_vec_temp=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_vecbounds=new_object_array(6);
	this.m_center_x=.0;
	this.m_center_y=.0;
	this.m_center_z=.0;
	this.m_culled=false;
	this.m_bones=[];
	this.m_distance_nearplane=.0;
	this.m_wireframe=false;
}
c_TMesh.prototype=extend_class(c_TEntity);
c_TMesh.m_new=function(){
	c_TEntity.m_new.call(this);
	return this;
}
c_TMesh.m_CreateMesh=function(t_parent_ent){
	var t_mesh=c_TMesh.m_new.call(new c_TMesh);
	t_mesh.m_classname="Mesh";
	if((t_parent_ent)!=null){
		t_mesh.p_AddParent(t_parent_ent);
	}
	t_mesh.m_entity_link=c_TEntity.m_entity_list.p_EntityListAdd(t_mesh);
	if(t_mesh.m_parent!=null){
		t_mesh.m_mat.p_Overwrite(t_mesh.m_parent.m_mat);
		t_mesh.p_UpdateMat(false);
	}else{
		t_mesh.p_UpdateMat(true);
	}
	return t_mesh;
}
c_TMesh.prototype.p_CreateSurfaceID=function(){
	this.m_no_surfs=this.m_no_surfs+1;
	return this.m_no_surfs-1;
}
c_TMesh.prototype.p_CreateSurface=function(t_bru){
	var t_surf=c_TSurface.m_new.call(new c_TSurface);
	this.m_surf_list.p_AddLast2(t_surf);
	if(t_bru!=null){
		t_surf.m_brush=t_bru.p_Copy();
	}
	t_surf.m_surf_id=this.p_CreateSurfaceID();
	this.m_anim_surf=resize_object_array(this.m_anim_surf,this.m_no_surfs);
	this.m_anim_surf_frame=resize_number_array(this.m_anim_surf_frame,this.m_no_surfs);
	this.m_reset_bounds=1;
	this.m_col_tree.m_reset_col_tree=1;
	return t_surf;
}
c_TMesh.prototype.p_AddSurface=function(t_surf){
	this.m_surf_list.p_AddLast2(t_surf);
	t_surf.m_surf_id=this.p_CreateSurfaceID();
	this.m_anim_surf=resize_object_array(this.m_anim_surf,this.m_no_surfs);
	this.m_anim_surf_frame=resize_number_array(this.m_anim_surf_frame,this.m_no_surfs);
	this.m_reset_bounds=1;
	this.m_col_tree.m_reset_col_tree=1;
	return t_surf;
}
c_TMesh.prototype.p_Draw=function(t_x,t_y,t_no_scaling){
	c_TRender.m_draw_list.p_AddLast3(this);
	var t_w=.0;
	var t_h=.0;
	if(this.m_parent!=null){
		this.p_EntityParent(null,true);
	}
	this.p_PositionEntity(t_x,t_y,1.99999,0);
	var t_spr=object_downcast((this),c_TSprite);
	if(!t_no_scaling && t_spr!=null){
		t_spr.m_pixel_scale[0]=1.0;
		t_spr.m_pixel_scale[1]=1.0;
		if((object_downcast((this),c_TText))!=null){
			var t_scx=((object_downcast((this),c_TText).m_char_pixels*object_downcast((this),c_TText).m_pixel_ratio+1.5)|0);
			t_spr.m_pixel_scale[0]=t_scx;
			t_spr.m_pixel_scale[1]=t_scx;
		}else{
			var t_scx2=(this.m_brush.p_GetTexture(0).m_width)*0.5;
			var t_scy=(this.m_brush.p_GetTexture(0).m_height)*0.5;
			t_spr.m_pixel_scale[0]=t_scx2;
			t_spr.m_pixel_scale[1]=t_scy;
		}
	}
	return 0;
}
c_TMesh.prototype.p_GetTotalTris=function(){
	var t_t=0;
	var t_=this.m_surf_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_s=t_.p_NextObject();
		t_t=t_t+t_s.m_no_tris;
	}
	return t_t;
}
c_TMesh.prototype.p_GetBounds=function(t_reset){
	if(this.m_reset_bounds==1 || t_reset==true){
		this.m_total_tris=this.p_GetTotalTris();
		this.m_reset_bounds=0;
		this.m_min_x=999999999.0;
		this.m_max_x=-999999999.0;
		this.m_min_y=999999999.0;
		this.m_max_y=-999999999.0;
		this.m_min_z=999999999.0;
		this.m_max_z=-999999999.0;
		var t_cc=0;
		var t_=this.m_surf_list.p_ObjectEnumerator();
		while(t_.p_HasNext()){
			var t_surf=t_.p_NextObject();
			t_cc+=1;
			for(var t_v=0;t_v<t_surf.m_no_verts;t_v=t_v+1){
				t_surf.m_vert_data.p_GetVertCoords(this.m_vec_temp,t_v);
				if(this.m_vec_temp.m_x<this.m_min_x){
					this.m_min_x=this.m_vec_temp.m_x;
					this.m_vecbounds[0]=this.m_vec_temp.p_Copy();
				}
				if(this.m_vec_temp.m_x>this.m_max_x){
					this.m_max_x=this.m_vec_temp.m_x;
					this.m_vecbounds[1]=this.m_vec_temp.p_Copy();
				}
				if(this.m_vec_temp.m_y<this.m_min_y){
					this.m_min_y=this.m_vec_temp.m_y;
					this.m_vecbounds[2]=this.m_vec_temp.p_Copy();
				}
				if(this.m_vec_temp.m_y>this.m_max_y){
					this.m_max_y=this.m_vec_temp.m_y;
					this.m_vecbounds[3]=this.m_vec_temp.p_Copy();
				}
				if(this.m_vec_temp.m_z<this.m_min_z){
					this.m_min_z=this.m_vec_temp.m_z;
					this.m_vecbounds[4]=this.m_vec_temp.p_Copy();
				}
				if(this.m_vec_temp.m_z>this.m_max_z){
					this.m_max_z=this.m_vec_temp.m_z;
					this.m_vecbounds[5]=this.m_vec_temp.p_Copy();
				}
			}
		}
		var t_width=this.m_max_x-this.m_min_x;
		var t_height=this.m_max_y-this.m_min_y;
		var t_depth=this.m_max_z-this.m_min_z;
		if(t_cc==0){
			t_width=0.0;
			t_height=0.0;
			t_depth=0.0;
		}
		if(this.m_cull_radius>=0.0){
			if(t_width>=t_height && t_width>=t_depth){
				this.m_cull_radius=t_width;
			}else{
				if(t_height>=t_width && t_height>=t_depth){
					this.m_cull_radius=t_height;
				}else{
					this.m_cull_radius=t_depth;
				}
			}
			this.m_cull_radius=this.m_cull_radius*0.5;
			var t_crs=this.m_cull_radius*this.m_cull_radius;
			this.m_cull_radius=Math.sqrt(t_crs+t_crs+t_crs);
		}
		this.m_center_x=this.m_min_x+t_width*0.5;
		this.m_center_y=this.m_min_y+t_height*0.5;
		this.m_center_z=this.m_min_z+t_depth*0.5;
	}
	return 0;
}
c_TMesh.prototype.p_GetSphereBounds=function(){
	if(this.m_cull_radius==0.0 || this.m_reset_bounds==1){
		this.p_GetBounds(false);
	}
	var t_center=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	var t_ss=this.m_vecbounds[0].p_Distance(t_center);
	for(var t_i=1;t_i<=5;t_i=t_i+1){
		var t_j=this.m_vecbounds[t_i].p_Distance(t_center);
		if(t_j>t_ss){
			t_ss=t_j;
		}
	}
	return t_ss;
}
c_TMesh.prototype.p_GetCulled=function(){
	return this.m_culled;
}
c_TMesh.prototype.p_GetAnimSurface=function(t_surf){
	return this.m_anim_surf[t_surf.m_surf_id];
}
c_TMesh.prototype.p_GetAnimSurface2=function(t_s){
	if(t_s<0 || t_s>this.m_anim_surf.length){
		return null;
	}
	return this.m_anim_surf[t_s-1];
}
c_TMesh.prototype.p_AutoFade=function(t_cam){
	var t_dist=t_cam.p_EntityDistance(this);
	if(t_dist>this.m_fade_near && t_dist<this.m_fade_far){
		this.m_fade_alpha=(t_dist-this.m_fade_near)/(this.m_fade_far-this.m_fade_near);
	}else{
		if(t_dist<this.m_fade_near){
			this.m_fade_alpha=0.0;
		}else{
			this.m_fade_alpha=1.0;
		}
	}
	return 0;
}
c_TMesh.prototype.p_Alpha=function(){
	var t_alpha=false;
	if(this.m_brush.m_alpha<1.0 || this.m_brush.m_blend==2 || this.m_brush.m_blend==3 || ((this.m_brush.m_fx&32)!=0)){
		t_alpha=true;
	}else{
		if(this.m_brush.m_tex[0]!=null){
			if((this.m_brush.m_tex[0].m_flags&2)!=0){
				t_alpha=true;
			}
		}
	}
	var t_=this.m_surf_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_surf=t_.p_NextObject();
		t_surf.m_alpha_enable=false;
		if(t_surf.m_brush!=null){
			if(t_surf.m_brush.m_alpha<1.0 || t_surf.m_brush.m_blend==2 || t_surf.m_brush.m_blend==3 || ((t_surf.m_brush.m_fx&32)!=0)){
				t_alpha=true;
			}else{
				if(t_surf.m_brush.m_tex[0]!=null){
					if((t_surf.m_brush.m_tex[0].m_flags&2)!=0){
						t_alpha=true;
					}
				}
			}
		}
		if(this.m_fade_alpha!=0.0){
			t_alpha=true;
		}
		if(t_alpha==true){
			t_surf.m_alpha_enable=true;
		}
	}
	this.m_using_alpha=t_alpha;
	return t_alpha;
}
c_TMesh.prototype.p_UpdateVertexAnimFrame=function(t_surf,t_orig_surf){
	if(!((t_surf)!=null)){
		return;
	}
	t_surf.m_anim_frame=this.m_anim_surf_frame[t_orig_surf.m_surf_id];
	t_surf.m_reset_vbo=t_surf.m_reset_vbo|1;
}
function c_Matrix(){
	Object.call(this);
	this.m_grid=new_array_array(4);
}
c_Matrix.m_new=function(){
	this.m_grid=[[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0]];
	return this;
}
c_Matrix.m_new2=function(t_a,t_b,t_c){
	this.m_grid[0]=[t_a.m_x,t_a.m_y,t_a.m_z,0.0];
	this.m_grid[1]=[t_b.m_x,t_b.m_y,t_b.m_z,0.0];
	this.m_grid[2]=[t_c.m_x,t_c.m_y,t_c.m_z,0.0];
	this.m_grid[3]=[0.0,0.0,0.0,1.0];
	return this;
}
c_Matrix.prototype.p_LoadIdentity=function(){
	this.m_grid[0][0]=1.0;
	this.m_grid[0][1]=0.0;
	this.m_grid[0][2]=0.0;
	this.m_grid[0][3]=0.0;
	this.m_grid[1][0]=0.0;
	this.m_grid[1][1]=1.0;
	this.m_grid[1][2]=0.0;
	this.m_grid[1][3]=0.0;
	this.m_grid[2][0]=0.0;
	this.m_grid[2][1]=0.0;
	this.m_grid[2][2]=1.0;
	this.m_grid[2][3]=0.0;
	this.m_grid[3][0]=0.0;
	this.m_grid[3][1]=0.0;
	this.m_grid[3][2]=0.0;
	this.m_grid[3][3]=1.0;
}
c_Matrix.prototype.p_Overwrite=function(t_mat){
	this.m_grid[0][0]=t_mat.m_grid[0][0];
	this.m_grid[1][0]=t_mat.m_grid[1][0];
	this.m_grid[2][0]=t_mat.m_grid[2][0];
	this.m_grid[3][0]=t_mat.m_grid[3][0];
	this.m_grid[0][1]=t_mat.m_grid[0][1];
	this.m_grid[1][1]=t_mat.m_grid[1][1];
	this.m_grid[2][1]=t_mat.m_grid[2][1];
	this.m_grid[3][1]=t_mat.m_grid[3][1];
	this.m_grid[0][2]=t_mat.m_grid[0][2];
	this.m_grid[1][2]=t_mat.m_grid[1][2];
	this.m_grid[2][2]=t_mat.m_grid[2][2];
	this.m_grid[3][2]=t_mat.m_grid[3][2];
	this.m_grid[0][3]=t_mat.m_grid[0][3];
	this.m_grid[1][3]=t_mat.m_grid[1][3];
	this.m_grid[2][3]=t_mat.m_grid[2][3];
	this.m_grid[3][3]=t_mat.m_grid[3][3];
}
c_Matrix.prototype.p_Translate=function(t_x,t_y,t_z){
	this.m_grid[3][0]=this.m_grid[0][0]*t_x+this.m_grid[1][0]*t_y+this.m_grid[2][0]*t_z+this.m_grid[3][0];
	this.m_grid[3][1]=this.m_grid[0][1]*t_x+this.m_grid[1][1]*t_y+this.m_grid[2][1]*t_z+this.m_grid[3][1];
	this.m_grid[3][2]=this.m_grid[0][2]*t_x+this.m_grid[1][2]*t_y+this.m_grid[2][2]*t_z+this.m_grid[3][2];
}
c_Matrix.prototype.p_Rotate=function(t_rx,t_ry,t_rz){
	var t_cos_ang=.0;
	var t_sin_ang=.0;
	var t_m20=.0;
	var t_m21=.0;
	var t_m22=.0;
	var t_m00=.0;
	var t_m01=.0;
	var t_m02=.0;
	var t_r1=.0;
	var t_r2=.0;
	var t_r3=.0;
	t_cos_ang=Math.cos((t_ry)*D2R);
	t_sin_ang=Math.sin((t_ry)*D2R);
	t_m00=this.m_grid[0][0]*t_cos_ang+this.m_grid[2][0]*-t_sin_ang;
	t_m01=this.m_grid[0][1]*t_cos_ang+this.m_grid[2][1]*-t_sin_ang;
	t_m02=this.m_grid[0][2]*t_cos_ang+this.m_grid[2][2]*-t_sin_ang;
	t_m20=this.m_grid[0][0]*t_sin_ang+this.m_grid[2][0]*t_cos_ang;
	t_m21=this.m_grid[0][1]*t_sin_ang+this.m_grid[2][1]*t_cos_ang;
	t_m22=this.m_grid[0][2]*t_sin_ang+this.m_grid[2][2]*t_cos_ang;
	t_cos_ang=Math.cos((t_rx)*D2R);
	t_sin_ang=Math.sin((t_rx)*D2R);
	var t_m10=this.m_grid[1][0]*t_cos_ang+t_m20*t_sin_ang;
	var t_m11=this.m_grid[1][1]*t_cos_ang+t_m21*t_sin_ang;
	var t_m12=this.m_grid[1][2]*t_cos_ang+t_m22*t_sin_ang;
	this.m_grid[2][0]=this.m_grid[1][0]*-t_sin_ang+t_m20*t_cos_ang;
	this.m_grid[2][1]=this.m_grid[1][1]*-t_sin_ang+t_m21*t_cos_ang;
	this.m_grid[2][2]=this.m_grid[1][2]*-t_sin_ang+t_m22*t_cos_ang;
	t_cos_ang=Math.cos((t_rz)*D2R);
	t_sin_ang=Math.sin((t_rz)*D2R);
	this.m_grid[0][0]=t_m00*t_cos_ang+t_m10*t_sin_ang;
	this.m_grid[0][1]=t_m01*t_cos_ang+t_m11*t_sin_ang;
	this.m_grid[0][2]=t_m02*t_cos_ang+t_m12*t_sin_ang;
	this.m_grid[1][0]=t_m00*-t_sin_ang+t_m10*t_cos_ang;
	this.m_grid[1][1]=t_m01*-t_sin_ang+t_m11*t_cos_ang;
	this.m_grid[1][2]=t_m02*-t_sin_ang+t_m12*t_cos_ang;
}
c_Matrix.prototype.p_Scale=function(t_sx,t_sy,t_sz){
	if(t_sx==1.0 && t_sy==1.0 && t_sz==1.0){
		return;
	}
	this.m_grid[0][0]=this.m_grid[0][0]*t_sx;
	this.m_grid[0][1]=this.m_grid[0][1]*t_sx;
	this.m_grid[0][2]=this.m_grid[0][2]*t_sx;
	this.m_grid[1][0]=this.m_grid[1][0]*t_sy;
	this.m_grid[1][1]=this.m_grid[1][1]*t_sy;
	this.m_grid[1][2]=this.m_grid[1][2]*t_sy;
	this.m_grid[2][0]=this.m_grid[2][0]*t_sz;
	this.m_grid[2][1]=this.m_grid[2][1]*t_sz;
	this.m_grid[2][2]=this.m_grid[2][2]*t_sz;
}
c_Matrix.prototype.p_Copy=function(){
	var t_mat=c_Matrix.m_new.call(new c_Matrix);
	t_mat.m_grid[0][0]=this.m_grid[0][0];
	t_mat.m_grid[1][0]=this.m_grid[1][0];
	t_mat.m_grid[2][0]=this.m_grid[2][0];
	t_mat.m_grid[3][0]=this.m_grid[3][0];
	t_mat.m_grid[0][1]=this.m_grid[0][1];
	t_mat.m_grid[1][1]=this.m_grid[1][1];
	t_mat.m_grid[2][1]=this.m_grid[2][1];
	t_mat.m_grid[3][1]=this.m_grid[3][1];
	t_mat.m_grid[0][2]=this.m_grid[0][2];
	t_mat.m_grid[1][2]=this.m_grid[1][2];
	t_mat.m_grid[2][2]=this.m_grid[2][2];
	t_mat.m_grid[3][2]=this.m_grid[3][2];
	t_mat.m_grid[0][3]=this.m_grid[0][3];
	t_mat.m_grid[1][3]=this.m_grid[1][3];
	t_mat.m_grid[2][3]=this.m_grid[2][3];
	t_mat.m_grid[3][3]=this.m_grid[3][3];
	return t_mat;
}
c_Matrix.prototype.p_Inverse=function(){
	var t_mat=c_Matrix.m_new.call(new c_Matrix);
	var t_tx=0.0;
	var t_ty=0.0;
	var t_tz=0.0;
	t_mat.m_grid[0][0]=this.m_grid[0][0];
	t_mat.m_grid[1][0]=this.m_grid[0][1];
	t_mat.m_grid[2][0]=this.m_grid[0][2];
	t_mat.m_grid[0][1]=this.m_grid[1][0];
	t_mat.m_grid[1][1]=this.m_grid[1][1];
	t_mat.m_grid[2][1]=this.m_grid[1][2];
	t_mat.m_grid[0][2]=this.m_grid[2][0];
	t_mat.m_grid[1][2]=this.m_grid[2][1];
	t_mat.m_grid[2][2]=this.m_grid[2][2];
	t_mat.m_grid[0][3]=0.0;
	t_mat.m_grid[1][3]=0.0;
	t_mat.m_grid[2][3]=0.0;
	t_mat.m_grid[3][3]=1.0;
	t_tx=this.m_grid[3][0];
	t_ty=this.m_grid[3][1];
	t_tz=this.m_grid[3][2];
	t_mat.m_grid[3][0]=-(this.m_grid[0][0]*t_tx+this.m_grid[0][1]*t_ty+this.m_grid[0][2]*t_tz);
	t_mat.m_grid[3][1]=-(this.m_grid[1][0]*t_tx+this.m_grid[1][1]*t_ty+this.m_grid[1][2]*t_tz);
	t_mat.m_grid[3][2]=-(this.m_grid[2][0]*t_tx+this.m_grid[2][1]*t_ty+this.m_grid[2][2]*t_tz);
	return t_mat;
}
c_Matrix.prototype.p_TransformPoint=function(t_x,t_y,t_z,t_w){
	var t_p0=.0;
	var t_p1=.0;
	var t_p2=.0;
	var t_p3=.0;
	t_p0=this.m_grid[0][0]*t_x+this.m_grid[1][0]*t_y+this.m_grid[2][0]*t_z+this.m_grid[3][0]*t_w;
	t_p1=this.m_grid[0][1]*t_x+this.m_grid[1][1]*t_y+this.m_grid[2][1]*t_z+this.m_grid[3][1]*t_w;
	t_p2=this.m_grid[0][2]*t_x+this.m_grid[1][2]*t_y+this.m_grid[2][2]*t_z+this.m_grid[3][2]*t_w;
	return [t_p0,t_p1,t_p2];
}
c_Matrix.prototype.p_Multiply=function(t_mat){
	var t_m00=this.m_grid[0][0]*t_mat.m_grid[0][0]+this.m_grid[1][0]*t_mat.m_grid[0][1]+this.m_grid[2][0]*t_mat.m_grid[0][2]+this.m_grid[3][0]*t_mat.m_grid[0][3];
	var t_m01=this.m_grid[0][1]*t_mat.m_grid[0][0]+this.m_grid[1][1]*t_mat.m_grid[0][1]+this.m_grid[2][1]*t_mat.m_grid[0][2]+this.m_grid[3][1]*t_mat.m_grid[0][3];
	var t_m02=this.m_grid[0][2]*t_mat.m_grid[0][0]+this.m_grid[1][2]*t_mat.m_grid[0][1]+this.m_grid[2][2]*t_mat.m_grid[0][2]+this.m_grid[3][2]*t_mat.m_grid[0][3];
	var t_m10=this.m_grid[0][0]*t_mat.m_grid[1][0]+this.m_grid[1][0]*t_mat.m_grid[1][1]+this.m_grid[2][0]*t_mat.m_grid[1][2]+this.m_grid[3][0]*t_mat.m_grid[1][3];
	var t_m11=this.m_grid[0][1]*t_mat.m_grid[1][0]+this.m_grid[1][1]*t_mat.m_grid[1][1]+this.m_grid[2][1]*t_mat.m_grid[1][2]+this.m_grid[3][1]*t_mat.m_grid[1][3];
	var t_m12=this.m_grid[0][2]*t_mat.m_grid[1][0]+this.m_grid[1][2]*t_mat.m_grid[1][1]+this.m_grid[2][2]*t_mat.m_grid[1][2]+this.m_grid[3][2]*t_mat.m_grid[1][3];
	var t_m20=this.m_grid[0][0]*t_mat.m_grid[2][0]+this.m_grid[1][0]*t_mat.m_grid[2][1]+this.m_grid[2][0]*t_mat.m_grid[2][2]+this.m_grid[3][0]*t_mat.m_grid[2][3];
	var t_m21=this.m_grid[0][1]*t_mat.m_grid[2][0]+this.m_grid[1][1]*t_mat.m_grid[2][1]+this.m_grid[2][1]*t_mat.m_grid[2][2]+this.m_grid[3][1]*t_mat.m_grid[2][3];
	var t_m22=this.m_grid[0][2]*t_mat.m_grid[2][0]+this.m_grid[1][2]*t_mat.m_grid[2][1]+this.m_grid[2][2]*t_mat.m_grid[2][2]+this.m_grid[3][2]*t_mat.m_grid[2][3];
	var t_m30=this.m_grid[0][0]*t_mat.m_grid[3][0]+this.m_grid[1][0]*t_mat.m_grid[3][1]+this.m_grid[2][0]*t_mat.m_grid[3][2]+this.m_grid[3][0]*t_mat.m_grid[3][3];
	var t_m31=this.m_grid[0][1]*t_mat.m_grid[3][0]+this.m_grid[1][1]*t_mat.m_grid[3][1]+this.m_grid[2][1]*t_mat.m_grid[3][2]+this.m_grid[3][1]*t_mat.m_grid[3][3];
	var t_m32=this.m_grid[0][2]*t_mat.m_grid[3][0]+this.m_grid[1][2]*t_mat.m_grid[3][1]+this.m_grid[2][2]*t_mat.m_grid[3][2]+this.m_grid[3][2]*t_mat.m_grid[3][3];
	this.m_grid[0][0]=t_m00;
	this.m_grid[0][1]=t_m01;
	this.m_grid[0][2]=t_m02;
	this.m_grid[1][0]=t_m10;
	this.m_grid[1][1]=t_m11;
	this.m_grid[1][2]=t_m12;
	this.m_grid[2][0]=t_m20;
	this.m_grid[2][1]=t_m21;
	this.m_grid[2][2]=t_m22;
	this.m_grid[3][0]=t_m30;
	this.m_grid[3][1]=t_m31;
	this.m_grid[3][2]=t_m32;
}
c_Matrix.prototype.p_Multiply2=function(t_v1){
	var t_v2=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	t_v2.m_x=this.m_grid[0][0]*t_v1.m_x+this.m_grid[1][0]*t_v1.m_y+this.m_grid[2][0]*t_v1.m_z;
	t_v2.m_y=this.m_grid[0][1]*t_v1.m_x+this.m_grid[1][1]*t_v1.m_y+this.m_grid[2][1]*t_v1.m_z;
	t_v2.m_z=this.m_grid[0][2]*t_v1.m_x+this.m_grid[1][2]*t_v1.m_y+this.m_grid[2][2]*t_v1.m_z;
	return t_v2;
}
c_Matrix.prototype.p_Multiply3=function(t_q){
	var t_t=this.p_Multiply2(t_q.m_o);
	return c_Line.m_new2.call(new c_Line,t_t,this.p_Multiply2(t_q.m_o.p_Add(t_q.m_d)).p_Subtract(t_t));
}
c_Matrix.prototype.p_CreateMatrix=function(t_rx,t_ry,t_rz,t_scx,t_scy,t_scz,t_px,t_py,t_pz){
	var t_sx=.0;
	var t_sy=.0;
	var t_sz=.0;
	var t_cx=.0;
	var t_cy=.0;
	var t_cz=.0;
	var t_theta=.0;
	t_sx=Math.sin((t_rx)*D2R);
	t_cx=Math.cos((t_rx)*D2R);
	t_sy=Math.sin((t_ry)*D2R);
	t_cy=Math.cos((t_ry)*D2R);
	t_sz=Math.sin((t_rz)*D2R);
	t_cz=Math.cos((t_rz)*D2R);
	var t_sycz=t_sy*t_cz;
	var t_cysz=t_cy*t_sz;
	var t_sysz=t_sy*t_sz;
	var t_cycz=t_cy*t_cz;
	this.m_grid[0][0]=(t_cycz+t_sysz*t_sx)*t_scx;
	this.m_grid[0][1]=t_cx*t_sz*t_scx;
	this.m_grid[0][2]=(-t_sycz+t_cysz*t_sx)*t_scx;
	this.m_grid[0][3]=0.0;
	this.m_grid[1][0]=(-t_cysz+t_sycz*t_sx)*t_scy;
	this.m_grid[1][1]=t_cx*t_cz*t_scy;
	this.m_grid[1][2]=(t_sysz+t_cycz*t_sx)*t_scy;
	this.m_grid[1][3]=0.0;
	this.m_grid[2][0]=t_sy*t_cx*t_scz;
	this.m_grid[2][1]=-t_sx*t_scz;
	this.m_grid[2][2]=t_cx*t_cy*t_scz;
	this.m_grid[2][3]=0.0;
	this.m_grid[3][0]=t_px;
	this.m_grid[3][1]=t_py;
	this.m_grid[3][2]=t_pz;
	this.m_grid[3][3]=1.0;
}
c_Matrix.prototype.p_FastRotateScale=function(t_rx,t_ry,t_rz,t_scx,t_scy,t_scz){
	this.p_CreateMatrix(t_rx,t_ry,t_rz,t_scx,t_scy,t_scz,this.m_grid[3][0],this.m_grid[3][1],this.m_grid[3][2]);
}
c_Matrix.prototype.p_Multiply4=function(t_mat){
	var t_m00=this.m_grid[0][0]*t_mat.m_grid[0][0]+this.m_grid[1][0]*t_mat.m_grid[0][1]+this.m_grid[2][0]*t_mat.m_grid[0][2]+this.m_grid[3][0]*t_mat.m_grid[0][3];
	var t_m01=this.m_grid[0][1]*t_mat.m_grid[0][0]+this.m_grid[1][1]*t_mat.m_grid[0][1]+this.m_grid[2][1]*t_mat.m_grid[0][2]+this.m_grid[3][1]*t_mat.m_grid[0][3];
	var t_m02=this.m_grid[0][2]*t_mat.m_grid[0][0]+this.m_grid[1][2]*t_mat.m_grid[0][1]+this.m_grid[2][2]*t_mat.m_grid[0][2]+this.m_grid[3][2]*t_mat.m_grid[0][3];
	var t_m03=this.m_grid[0][3]*t_mat.m_grid[0][0]+this.m_grid[1][3]*t_mat.m_grid[0][1]+this.m_grid[2][3]*t_mat.m_grid[0][2]+this.m_grid[3][3]*t_mat.m_grid[0][3];
	var t_m10=this.m_grid[0][0]*t_mat.m_grid[1][0]+this.m_grid[1][0]*t_mat.m_grid[1][1]+this.m_grid[2][0]*t_mat.m_grid[1][2]+this.m_grid[3][0]*t_mat.m_grid[1][3];
	var t_m11=this.m_grid[0][1]*t_mat.m_grid[1][0]+this.m_grid[1][1]*t_mat.m_grid[1][1]+this.m_grid[2][1]*t_mat.m_grid[1][2]+this.m_grid[3][1]*t_mat.m_grid[1][3];
	var t_m12=this.m_grid[0][2]*t_mat.m_grid[1][0]+this.m_grid[1][2]*t_mat.m_grid[1][1]+this.m_grid[2][2]*t_mat.m_grid[1][2]+this.m_grid[3][2]*t_mat.m_grid[1][3];
	var t_m13=this.m_grid[0][3]*t_mat.m_grid[1][0]+this.m_grid[1][3]*t_mat.m_grid[1][1]+this.m_grid[2][3]*t_mat.m_grid[1][2]+this.m_grid[3][3]*t_mat.m_grid[1][3];
	var t_m20=this.m_grid[0][0]*t_mat.m_grid[2][0]+this.m_grid[1][0]*t_mat.m_grid[2][1]+this.m_grid[2][0]*t_mat.m_grid[2][2]+this.m_grid[3][0]*t_mat.m_grid[2][3];
	var t_m21=this.m_grid[0][1]*t_mat.m_grid[2][0]+this.m_grid[1][1]*t_mat.m_grid[2][1]+this.m_grid[2][1]*t_mat.m_grid[2][2]+this.m_grid[3][1]*t_mat.m_grid[2][3];
	var t_m22=this.m_grid[0][2]*t_mat.m_grid[2][0]+this.m_grid[1][2]*t_mat.m_grid[2][1]+this.m_grid[2][2]*t_mat.m_grid[2][2]+this.m_grid[3][2]*t_mat.m_grid[2][3];
	var t_m23=this.m_grid[0][3]*t_mat.m_grid[2][0]+this.m_grid[1][3]*t_mat.m_grid[2][1]+this.m_grid[2][3]*t_mat.m_grid[2][2]+this.m_grid[3][3]*t_mat.m_grid[2][3];
	var t_m30=this.m_grid[0][0]*t_mat.m_grid[3][0]+this.m_grid[1][0]*t_mat.m_grid[3][1]+this.m_grid[2][0]*t_mat.m_grid[3][2]+this.m_grid[3][0]*t_mat.m_grid[3][3];
	var t_m31=this.m_grid[0][1]*t_mat.m_grid[3][0]+this.m_grid[1][1]*t_mat.m_grid[3][1]+this.m_grid[2][1]*t_mat.m_grid[3][2]+this.m_grid[3][1]*t_mat.m_grid[3][3];
	var t_m32=this.m_grid[0][2]*t_mat.m_grid[3][0]+this.m_grid[1][2]*t_mat.m_grid[3][1]+this.m_grid[2][2]*t_mat.m_grid[3][2]+this.m_grid[3][2]*t_mat.m_grid[3][3];
	var t_m33=this.m_grid[0][3]*t_mat.m_grid[3][0]+this.m_grid[1][3]*t_mat.m_grid[3][1]+this.m_grid[2][3]*t_mat.m_grid[3][2]+this.m_grid[3][3]*t_mat.m_grid[3][3];
	this.m_grid[0][0]=t_m00;
	this.m_grid[0][1]=t_m01;
	this.m_grid[0][2]=t_m02;
	this.m_grid[0][3]=t_m03;
	this.m_grid[1][0]=t_m10;
	this.m_grid[1][1]=t_m11;
	this.m_grid[1][2]=t_m12;
	this.m_grid[1][3]=t_m13;
	this.m_grid[2][0]=t_m20;
	this.m_grid[2][1]=t_m21;
	this.m_grid[2][2]=t_m22;
	this.m_grid[2][3]=t_m23;
	this.m_grid[3][0]=t_m30;
	this.m_grid[3][1]=t_m31;
	this.m_grid[3][2]=t_m32;
	this.m_grid[3][3]=t_m33;
}
c_Matrix.prototype.p_Multiply42=function(t_v1){
	var t_v2=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	t_v2.m_x=this.m_grid[0][0]*t_v1.m_x+this.m_grid[1][0]*t_v1.m_y+this.m_grid[2][0]*t_v1.m_z+this.m_grid[3][0];
	t_v2.m_y=this.m_grid[0][1]*t_v1.m_x+this.m_grid[1][1]*t_v1.m_y+this.m_grid[2][1]*t_v1.m_z+this.m_grid[3][1];
	t_v2.m_z=this.m_grid[0][2]*t_v1.m_x+this.m_grid[1][2]*t_v1.m_y+this.m_grid[2][2]*t_v1.m_z+this.m_grid[3][2];
	return t_v2;
}
c_Matrix.prototype.p_ToArray=function(){
	var t_arr=new_number_array(16);
	t_arr[0]=this.m_grid[0][0];
	t_arr[1]=this.m_grid[0][1];
	t_arr[2]=this.m_grid[0][2];
	t_arr[3]=this.m_grid[0][3];
	t_arr[4]=this.m_grid[1][0];
	t_arr[5]=this.m_grid[1][1];
	t_arr[6]=this.m_grid[1][2];
	t_arr[7]=this.m_grid[1][3];
	t_arr[8]=this.m_grid[2][0];
	t_arr[9]=this.m_grid[2][1];
	t_arr[10]=this.m_grid[2][2];
	t_arr[11]=this.m_grid[2][3];
	t_arr[12]=this.m_grid[3][0];
	t_arr[13]=this.m_grid[3][1];
	t_arr[14]=this.m_grid[3][2];
	t_arr[15]=this.m_grid[3][3];
	return t_arr;
}
c_Matrix.prototype.p_ToArray2=function(t_arr){
	t_arr[0]=this.m_grid[0][0];
	t_arr[1]=this.m_grid[0][1];
	t_arr[2]=this.m_grid[0][2];
	t_arr[3]=this.m_grid[0][3];
	t_arr[4]=this.m_grid[1][0];
	t_arr[5]=this.m_grid[1][1];
	t_arr[6]=this.m_grid[1][2];
	t_arr[7]=this.m_grid[1][3];
	t_arr[8]=this.m_grid[2][0];
	t_arr[9]=this.m_grid[2][1];
	t_arr[10]=this.m_grid[2][2];
	t_arr[11]=this.m_grid[2][3];
	t_arr[12]=this.m_grid[3][0];
	t_arr[13]=this.m_grid[3][1];
	t_arr[14]=this.m_grid[3][2];
	t_arr[15]=this.m_grid[3][3];
}
c_Matrix.prototype.p_RotateRoll=function(t_ang){
	var t_cos_ang=Math.cos((t_ang)*D2R);
	var t_sin_ang=Math.sin((t_ang)*D2R);
	var t_m00=this.m_grid[0][0]*t_cos_ang+this.m_grid[1][0]*t_sin_ang;
	var t_m01=this.m_grid[0][1]*t_cos_ang+this.m_grid[1][1]*t_sin_ang;
	var t_m02=this.m_grid[0][2]*t_cos_ang+this.m_grid[1][2]*t_sin_ang;
	this.m_grid[1][0]=this.m_grid[0][0]*-t_sin_ang+this.m_grid[1][0]*t_cos_ang;
	this.m_grid[1][1]=this.m_grid[0][1]*-t_sin_ang+this.m_grid[1][1]*t_cos_ang;
	this.m_grid[1][2]=this.m_grid[0][2]*-t_sin_ang+this.m_grid[1][2]*t_cos_ang;
	this.m_grid[0][0]=t_m00;
	this.m_grid[0][1]=t_m01;
	this.m_grid[0][2]=t_m02;
}
function c_Vector(){
	Object.call(this);
	this.m_x=.0;
	this.m_y=.0;
	this.m_z=.0;
}
c_Vector.m_new=function(t_xx,t_yy,t_zz){
	this.m_x=t_xx;
	this.m_y=t_yy;
	this.m_z=t_zz;
	return this;
}
c_Vector.prototype.p_Add=function(t_vec){
	return c_Vector.m_new.call(new c_Vector,this.m_x+t_vec.m_x,this.m_y+t_vec.m_y,this.m_z+t_vec.m_z);
}
c_Vector.prototype.p_Add2=function(t_vx,t_vy,t_vz){
	return c_Vector.m_new.call(new c_Vector,this.m_x+t_vx,this.m_y+t_vy,this.m_z+t_vz);
}
c_Vector.prototype.p_Subtract=function(t_vec){
	return c_Vector.m_new.call(new c_Vector,this.m_x-t_vec.m_x,this.m_y-t_vec.m_y,this.m_z-t_vec.m_z);
}
c_Vector.prototype.p_Subtract2=function(t_vx,t_vy,t_vz){
	return c_Vector.m_new.call(new c_Vector,this.m_x-t_vx,this.m_y-t_vy,this.m_z-t_vz);
}
c_Vector.prototype.p_Copy=function(){
	return c_Vector.m_new.call(new c_Vector,this.m_x,this.m_y,this.m_z);
}
c_Vector.prototype.p_Normalize=function(){
	if(this.m_x==0.0 && this.m_y==0.0 && this.m_z==0.0){
		return c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	}
	var t_d=1.0/Math.sqrt(this.m_x*this.m_x+this.m_y*this.m_y+this.m_z*this.m_z);
	return c_Vector.m_new.call(new c_Vector,this.m_x*t_d,this.m_y*t_d,this.m_z*t_d);
}
c_Vector.prototype.p_Dot=function(t_vec){
	return this.m_x*t_vec.m_x+this.m_y*t_vec.m_y+this.m_z*t_vec.m_z;
}
c_Vector.prototype.p_Cross=function(t_vec){
	return c_Vector.m_new.call(new c_Vector,this.m_y*t_vec.m_z-this.m_z*t_vec.m_y,this.m_z*t_vec.m_x-this.m_x*t_vec.m_z,this.m_x*t_vec.m_y-this.m_y*t_vec.m_x);
}
c_Vector.prototype.p_Update=function(t_xx,t_yy,t_zz){
	this.m_x=t_xx;
	this.m_y=t_yy;
	this.m_z=t_zz;
	return 0;
}
c_Vector.prototype.p_Overwrite2=function(t_xx,t_yy,t_zz){
	this.m_x=t_xx;
	this.m_y=t_yy;
	this.m_z=t_zz;
	return 0;
}
c_Vector.prototype.p_Overwrite3=function(t_v){
	this.m_x=t_v.m_x;
	this.m_y=t_v.m_y;
	this.m_z=t_v.m_z;
	return 0;
}
c_Vector.prototype.p_Distance=function(t_q){
	var t_xx=this.m_x-t_q.m_x;
	var t_yy=this.m_y-t_q.m_y;
	var t_zz=this.m_z-t_q.m_z;
	return Math.sqrt(t_xx*t_xx+t_yy*t_yy+t_zz*t_zz);
}
c_Vector.prototype.p_Length=function(){
	return Math.sqrt(this.m_x*this.m_x+this.m_y*this.m_y+this.m_z*this.m_z);
}
c_Vector.prototype.p_DistanceSquared=function(){
	return this.m_x*this.m_x+this.m_y*this.m_y+this.m_z*this.m_z;
}
c_Vector.prototype.p_DistanceSquared2=function(t_q){
	var t_xx=this.m_x-t_q.m_x;
	var t_yy=this.m_y-t_q.m_y;
	var t_zz=this.m_z-t_q.m_z;
	return t_xx*t_xx+t_yy*t_yy+t_zz*t_zz;
}
c_Vector.prototype.p_Multiply2=function(t_v){
	return c_Vector.m_new.call(new c_Vector,this.m_x*t_v.m_x,this.m_y*t_v.m_y,this.m_z*t_v.m_z);
}
c_Vector.prototype.p_Multiply5=function(t_val){
	return c_Vector.m_new.call(new c_Vector,this.m_x*t_val,this.m_y*t_val,this.m_z*t_val);
}
c_Vector.prototype.p_Multiply6=function(t_a,t_b,t_c){
	return c_Vector.m_new.call(new c_Vector,this.m_x*t_a,this.m_y*t_b,this.m_z*t_c);
}
c_Vector.prototype.p_PointOnSegment=function(t_o,t_d){
	var t_d2=t_d.p_Subtract(t_o);
	var t_dx=this.m_x-t_o.m_x;
	var t_dy=this.m_y-t_o.m_y;
	var t_dz=this.m_z-t_o.m_z;
	var t_v=(t_d2.m_x*t_dx+t_d2.m_y*t_dy+t_d2.m_z*t_dz)/(t_d2.m_x*t_d2.m_x+t_d2.m_y*t_d2.m_y+t_d2.m_z*t_d2.m_z);
	if(t_v>1.0){
		return t_d;
	}
	if(t_v<0.0){
		return t_o;
	}
	return c_Vector.m_new.call(new c_Vector,t_o.m_x+t_d2.m_x*t_v,t_o.m_y+t_d2.m_y*t_v,t_o.m_z+t_d2.m_z*t_v);
}
function c_TCollision(){
	Object.call(this);
	this.m_updated_shape=false;
	this.m_no_collisions=0;
	this.m_impact=[];
	this.m_radius_x=0.0;
	this.m_radius_y=0.0;
	this.m_box_w=0.0;
	this.m_box_x=0.0;
	this.m_box_y=0.0;
	this.m_box_z=0.0;
	this.m_box_h=0.0;
	this.m_box_d=0.0;
	this.m_old_px=.0;
	this.m_old_py=.0;
	this.m_old_pz=.0;
	this.m_old_rz=.0;
	this.m_old_ry=.0;
	this.m_old_rx=.0;
	this.m_sa=new_number_array(6);
	this.m_old_dst_radius=.0;
	this.m_old_gsx=.0;
	this.m_old_gsy=.0;
	this.m_old_gsz=.0;
	this.m_old_igsx=.0;
	this.m_old_igsy=.0;
	this.m_old_igsz=.0;
}
c_TCollision.m_new=function(){
	return this;
}
c_TCollision.prototype.p_ClearImpact=function(){
	this.m_no_collisions=0;
	this.m_impact=new_object_array(0);
}
c_TCollision.prototype.p_MoveTest=function(t_ent){
	if(this.m_old_px==t_ent.m_mat.m_grid[3][0] && this.m_old_py==t_ent.m_mat.m_grid[3][1] && this.m_old_pz==-t_ent.m_mat.m_grid[3][2]){
		return false;
	}
	return true;
}
c_TCollision.prototype.p_SetOldPosition=function(t_ent,t_x,t_y,t_z){
	this.m_old_px=t_ent.m_mat.m_grid[3][0];
	this.m_old_py=t_ent.m_mat.m_grid[3][1];
	this.m_old_pz=-t_ent.m_mat.m_grid[3][2];
	this.m_old_rz=t_ent.m_rz;
	this.m_old_ry=t_ent.m_ry;
	this.m_old_rx=t_ent.m_rx;
}
c_TCollision.prototype.p_CreateImpact=function(t_ent2_hit,t_coll_obj){
	this.m_no_collisions=this.m_no_collisions+1;
	var t_i=this.m_no_collisions-1;
	this.m_impact=resize_object_array(this.m_impact,t_i+1);
	this.m_impact[t_i]=c_TCollisionImpact.m_new.call(new c_TCollisionImpact);
	this.m_impact[t_i].m_x=t_coll_obj.m_col_coords.m_x;
	this.m_impact[t_i].m_y=t_coll_obj.m_col_coords.m_y;
	this.m_impact[t_i].m_z=t_coll_obj.m_col_coords.m_z;
	this.m_impact[t_i].m_nx=t_coll_obj.m_normal.m_x;
	this.m_impact[t_i].m_ny=t_coll_obj.m_normal.m_y;
	this.m_impact[t_i].m_nz=t_coll_obj.m_normal.m_z;
	this.m_impact[t_i].m_ent=t_ent2_hit;
	if(object_downcast((t_ent2_hit),c_TMesh)!=null){
		this.m_impact[t_i].m_surf=t_coll_obj.m_surface;
	}else{
		this.m_impact[t_i].m_surf=0;
	}
	this.m_impact[t_i].m_tri=t_coll_obj.m_index;
}
function c_List(){
	Object.call(this);
	this.m__head=(c_HeadNode.m_new.call(new c_HeadNode));
}
c_List.m_new=function(){
	return this;
}
c_List.prototype.p_AddLast=function(t_data){
	return c_Node2.m_new.call(new c_Node2,this.m__head,this.m__head.m__pred,t_data);
}
c_List.m_new2=function(t_data){
	var t_=t_data;
	var t_2=0;
	while(t_2<t_.length){
		var t_t=t_[t_2];
		t_2=t_2+1;
		this.p_AddLast(t_t);
	}
	return this;
}
c_List.prototype.p_FirstNode=function(){
	if(this.m__head.m__succ!=this.m__head){
		return this.m__head.m__succ;
	}
	return null;
}
c_List.prototype.p_AddFirst=function(t_data){
	return c_Node2.m_new.call(new c_Node2,this.m__head.m__succ,this.m__head,t_data);
}
c_List.prototype.p_IsEmpty=function(){
	return this.m__head.m__succ==this.m__head;
}
c_List.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator2.m_new.call(new c_Enumerator2,this);
}
function c_EntityList(){
	c_List.call(this);
}
c_EntityList.prototype=extend_class(c_List);
c_EntityList.m_new=function(){
	c_List.m_new.call(this);
	return this;
}
c_EntityList.prototype.p_EntityListAdd=function(t_obj){
	var t_llink=this.p_FirstNode();
	if(t_obj.m_order>0){
		t_llink=this.p_AddFirst(t_obj);
		return t_llink;
	}else{
		t_llink=this.p_AddLast(t_obj);
		return t_llink;
	}
}
function c_Node2(){
	Object.call(this);
	this.m__succ=null;
	this.m__pred=null;
	this.m__data=null;
}
c_Node2.m_new=function(t_succ,t_pred,t_data){
	this.m__succ=t_succ;
	this.m__pred=t_pred;
	this.m__succ.m__pred=this;
	this.m__pred.m__succ=this;
	this.m__data=t_data;
	return this;
}
c_Node2.m_new2=function(){
	return this;
}
c_Node2.prototype.p_Remove=function(){
	this.m__succ.m__pred=this.m__pred;
	this.m__pred.m__succ=this.m__succ;
	return 0;
}
function c_HeadNode(){
	c_Node2.call(this);
}
c_HeadNode.prototype=extend_class(c_Node2);
c_HeadNode.m_new=function(){
	c_Node2.m_new2.call(this);
	this.m__succ=(this);
	this.m__pred=(this);
	return this;
}
function bb_functions_CreateMesh(t_parent){
	return c_TMesh.m_CreateMesh(t_parent);
}
function c_TBrush(){
	Object.call(this);
	this.m_red=1.0;
	this.m_green=1.0;
	this.m_blue=1.0;
	this.m_tex=new_object_array(8);
	this.m_no_texs=0;
	this.m_fx=0;
	this.m_name="";
	this.m_alpha=1.0;
	this.m_shine=0.05;
	this.m_shine_strength=100.0;
	this.m_blend=0;
}
c_TBrush.m_new=function(){
	return this;
}
c_TBrush.m_new2=function(t_hexcolor){
	this.m_red=((t_hexcolor&16711680)>>16)*0.0039215686274509803;
	this.m_green=((t_hexcolor&65280)>>8)*0.0039215686274509803;
	this.m_blue=(t_hexcolor&255)*0.0039215686274509803;
	this.m_tex[0]=c_TTexture.m_new.call(new c_TTexture);
	return this;
}
c_TBrush.m_new3=function(t_r,t_g,t_b){
	this.m_red=(t_r)*0.0039215686274509803;
	this.m_green=(t_g)*0.0039215686274509803;
	this.m_blue=(t_b)*0.0039215686274509803;
	this.m_tex[0]=c_TTexture.m_new.call(new c_TTexture);
	return this;
}
c_TBrush.prototype.p_BrushTexture=function(t_texture,t_frame,t_index){
	this.m_tex[t_index]=t_texture;
	if(t_index+1>this.m_no_texs){
		this.m_no_texs=t_index+1;
	}
	if(t_frame<0){
		t_frame=0;
	}
	if(t_frame>t_texture.m_no_frames-1){
		t_frame=t_texture.m_no_frames-1;
	}
	t_texture.m_tex_frame=t_frame;
	if(t_frame>0 && t_texture.m_no_frames>1){
		var t_x=t_frame % t_texture.m_frame_xstep;
		var t_y=((t_frame/t_texture.m_frame_ystep)|0) % t_texture.m_frame_ystep;
		t_texture.m_u_pos=(t_x)*t_texture.m_frame_ustep;
		t_texture.m_v_pos=(t_y)*t_texture.m_frame_vstep;
	}
}
c_TBrush.m_new4=function(t_texture){
	this.p_BrushTexture(t_texture,0,0);
	return this;
}
c_TBrush.prototype.p_Copy=function(){
	var t_brush=c_TBrush.m_new.call(new c_TBrush);
	t_brush.m_no_texs=this.m_no_texs;
	t_brush.m_name=this.m_name;
	t_brush.m_red=this.m_red;
	t_brush.m_green=this.m_green;
	t_brush.m_blue=this.m_blue;
	t_brush.m_alpha=this.m_alpha;
	t_brush.m_shine=this.m_shine;
	t_brush.m_shine_strength=this.m_shine_strength;
	t_brush.m_blend=this.m_blend;
	t_brush.m_fx=this.m_fx;
	if((this.m_tex[0])!=null){
		t_brush.m_tex[0]=this.m_tex[0];
	}
	if((this.m_tex[1])!=null){
		t_brush.m_tex[1]=this.m_tex[1];
	}
	if((this.m_tex[2])!=null){
		t_brush.m_tex[2]=this.m_tex[2];
	}
	if((this.m_tex[3])!=null){
		t_brush.m_tex[3]=this.m_tex[3];
	}
	if((this.m_tex[4])!=null){
		t_brush.m_tex[4]=this.m_tex[4];
	}
	if((this.m_tex[5])!=null){
		t_brush.m_tex[5]=this.m_tex[5];
	}
	if((this.m_tex[6])!=null){
		t_brush.m_tex[6]=this.m_tex[6];
	}
	if((this.m_tex[7])!=null){
		t_brush.m_tex[7]=this.m_tex[7];
	}
	return t_brush;
}
c_TBrush.prototype.p_GetTexture=function(t_index){
	return this.m_tex[t_index];
}
function c_TTexture(){
	Object.call(this);
	this.m_resize_smooth=false;
	this.m_no_frames=1;
	this.m_tex_frame=0;
	this.m_frame_xstep=0;
	this.m_frame_ystep=0;
	this.m_frame_ustep=1.0;
	this.m_u_pos=.0;
	this.m_frame_vstep=1.0;
	this.m_v_pos=.0;
	this.m_file="";
	this.m_flags=0;
	this.m_tex_link=null;
	this.m_pixmap=null;
	this.m_orig_width=0;
	this.m_orig_height=0;
	this.m_width=0;
	this.m_height=0;
	this.m_frame_startx=0;
	this.m_frame_starty=0;
	this.m_u_scale=1.0;
	this.m_v_scale=1.0;
	this.m_bind_flags=-1;
	this.m_is_font=false;
	this.m_blend=2;
	this.m_gltex=new_number_array(1);
	this.m_freeMemoryAfterBind=false;
	this.m_coords=0;
	this.m_angle=.0;
	this.m_cube_mode=1;
	this.m_tex_smooth=true;
	this.m_no_mipmaps=0;
}
c_TTexture.m_useGlobalResizeSmooth=false;
c_TTexture.m_new=function(){
	this.m_resize_smooth=c_TTexture.m_useGlobalResizeSmooth;
	return this;
}
c_TTexture.prototype.p_FilterFlags=function(){
	var t_=c_TTextureFilter.m_filter_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_filter=t_.p_NextObject();
		var t_len1=t_filter.m_text.length;
		var t_len2=this.m_file.length-t_len1;
		var t_file2=this.m_file.slice(t_len2);
		if(t_file2==t_filter.m_text){
			this.m_flags=this.m_flags|t_filter.m_flags;
		}
	}
	return 0;
}
c_TTexture.m_tex_list=null;
c_TTexture.prototype.p_TexInList=function(){
	var t_=c_TTexture.m_tex_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_tex=t_.p_NextObject();
		if(this.m_file==t_tex.m_file && this.m_flags==t_tex.m_flags){
			return t_tex;
		}
	}
	return null;
}
c_TTexture.m_Pow2Size=function(t_n){
	var t_t=1;
	while(t_t<t_n){
		t_t=t_t<<1;
	}
	return t_t;
}
c_TTexture.m_AdjustPixmap=function(t_pixmap,t_resize_smooth,t_tex){
	var t_width=c_TTexture.m_Pow2Size(t_pixmap.m_width);
	var t_height=c_TTexture.m_Pow2Size(t_pixmap.m_height);
	if(t_width!=t_pixmap.m_width || t_height!=t_pixmap.m_height){
		if(t_resize_smooth){
			t_pixmap=t_pixmap.p_ResizePixmap(t_width,t_height);
		}else{
			t_pixmap=t_pixmap.p_ResizePixmapNoSmooth(t_width,t_height);
		}
	}
	return t_pixmap;
}
c_TTexture.prototype.p_SetAnimationFrames=function(t_first_frame,t_frame_count,t_frame_width,t_frame_height){
	this.m_frame_xstep=((this.m_pixmap.m_width/t_frame_width)|0);
	this.m_frame_ystep=((this.m_pixmap.m_height/t_frame_height)|0);
	this.m_frame_startx=t_first_frame % this.m_frame_xstep;
	this.m_frame_starty=((t_first_frame/this.m_frame_ystep)|0) % this.m_frame_ystep;
	if(t_frame_count<0){
		t_frame_count=this.m_frame_xstep*this.m_frame_ystep;
	}
	this.m_no_frames=t_frame_count;
	if(this.m_no_frames>1){
		this.m_frame_ustep=1.0/(this.m_frame_xstep);
		this.m_frame_vstep=1.0/(this.m_frame_ystep);
		this.m_u_scale=this.m_frame_ustep;
		this.m_v_scale=this.m_frame_vstep;
		this.m_u_pos=(this.m_frame_startx)*this.m_frame_ustep;
		this.m_v_pos=(this.m_frame_starty)*this.m_frame_vstep;
	}
}
c_TTexture.m_tex_bind_stack=null;
c_TTexture.m_PushBindTexture=function(t_tex,t_flags){
	t_tex.m_bind_flags=t_flags;
	c_TTexture.m_tex_bind_stack.p_Push7(t_tex);
	return 0;
}
c_TTexture.m_LoadAnimTexture=function(t_file,t_flags,t_frame_width,t_frame_height,t_first_frame,t_frame_count,t_tex,t_force_new){
	if(t_tex==null){
		t_tex=c_TTexture.m_new.call(new c_TTexture);
	}
	t_tex.m_file=t_file;
	t_tex.p_FilterFlags();
	if(t_flags>-1){
		t_tex.m_flags=t_flags;
	}
	var t_old_tex=null;
	if(!((t_force_new)!=0)){
		t_old_tex=t_tex.p_TexInList();
	}
	if(t_old_tex!=null && t_old_tex!=t_tex){
		return t_old_tex;
	}else{
		if(t_old_tex!=t_tex){
			t_tex.m_tex_link=c_TTexture.m_tex_list.p_AddLast9(t_tex);
		}
	}
	var t_new_scx=1.0;
	var t_new_scy=1.0;
	var t_oldw=0;
	var t_oldh=0;
	t_tex.m_pixmap=c_TPixmap.m_LoadPixmap(t_file);
	if(t_tex.m_pixmap.m_height==0){
		return t_tex;
	}
	t_oldw=t_tex.m_pixmap.m_width;
	t_oldh=t_tex.m_pixmap.m_height;
	t_tex.m_orig_width=t_oldw;
	t_tex.m_orig_height=t_oldh;
	if((t_tex.m_flags&256)==0){
		t_tex.m_pixmap=c_TTexture.m_AdjustPixmap(t_tex.m_pixmap,t_tex.m_resize_smooth,t_tex);
	}
	t_tex.m_width=t_tex.m_pixmap.m_width;
	t_tex.m_height=t_tex.m_pixmap.m_height;
	if(t_oldw!=t_tex.m_width || t_oldh!=t_tex.m_height){
		t_new_scx=(t_tex.m_width)/(t_oldw);
		t_new_scy=(t_tex.m_height)/(t_oldh);
	}
	if(t_frame_width==0 && t_frame_height==0){
		t_frame_width=t_tex.m_pixmap.m_width;
		t_frame_height=t_tex.m_pixmap.m_height;
	}else{
		t_frame_width=(((t_frame_width)*t_new_scx)|0);
		t_frame_height=(((t_frame_height)*t_new_scy)|0);
	}
	t_tex.p_SetAnimationFrames(t_first_frame,t_frame_count,t_frame_width,t_frame_height);
	c_TTexture.m_PushBindTexture(t_tex,t_flags);
	return t_tex;
}
c_TTexture.m_LoadTexture=function(t_file,t_flags,t_tex){
	return c_TTexture.m_LoadAnimTexture(t_file,t_flags,0,0,0,1,t_tex,0);
}
c_TTexture.m_LoadTexture2=function(t_pixmap,t_flags,t_tex){
	if(!((t_tex)!=null)){
		t_tex=c_TTexture.m_new.call(new c_TTexture);
		t_tex.m_tex_link=c_TTexture.m_tex_list.p_AddLast9(t_tex);
	}
	t_tex.p_FilterFlags();
	if(t_flags>-1){
		t_tex.m_flags=t_flags;
	}
	if(t_pixmap==null){
		return t_tex;
	}
	t_tex.m_pixmap=t_pixmap;
	if(t_tex.m_pixmap.m_height==0){
		return t_tex;
	}
	if((t_tex.m_flags&256)==0){
		t_tex.m_pixmap=c_TTexture.m_AdjustPixmap(t_tex.m_pixmap,t_tex.m_resize_smooth,t_tex);
	}
	t_tex.m_width=t_tex.m_pixmap.m_width;
	t_tex.m_height=t_tex.m_pixmap.m_height;
	c_TTexture.m_PushBindTexture(t_tex,t_flags);
	return t_tex;
}
c_TTexture.prototype.p_TextureBlend=function(t_blend_no){
	this.m_blend=t_blend_no;
	return 0;
}
c_TTexture.prototype.p_FreeTexture_=function(){
	this.m_tex_link.p_Remove();
	if((this.m_pixmap)!=null){
		this.m_pixmap.p_DecBind();
		if(this.m_pixmap.p_GetBindCount()==0){
			c_TPixmap.m_preloader.p_RemoveFromStack(this.m_file);
			this.m_pixmap.p_FreePixmap();
		}
	}
	this.m_gltex[0]=0;
	return 0;
}
c_TTexture.m_TextureFilter=function(t_match_text,t_flags){
	var t_filter=c_TTextureFilter.m_new.call(new c_TTextureFilter);
	t_filter.m_text=t_match_text;
	t_filter.m_flags=t_flags;
	c_TTextureFilter.m_filter_list.p_AddLast8(t_filter);
	return 0;
}
c_TTexture.m_CreateTexture=function(t_width,t_height,t_flags,t_frames,t_tex){
	if(t_tex==null){
		t_tex=c_TTexture.m_new.call(new c_TTexture);
		t_tex.m_tex_link=c_TTexture.m_tex_list.p_AddLast9(t_tex);
	}
	t_width=c_TTexture.m_Pow2Size(t_width);
	t_height=c_TTexture.m_Pow2Size(t_height);
	t_tex.m_pixmap=c_TPixmap.m_CreatePixmap(t_width*t_frames,t_height,4);
	t_tex.m_flags=t_flags;
	t_tex.m_no_frames=t_frames;
	t_tex.m_gltex=t_tex.m_gltex.slice(0,t_tex.m_no_frames);
	var t_pixmap=t_tex.m_pixmap;
	if((t_tex.m_flags&256)==0){
		t_pixmap=c_TTexture.m_AdjustPixmap(t_pixmap,t_tex.m_resize_smooth,t_tex);
	}
	t_tex.m_width=t_pixmap.m_width;
	t_tex.m_height=t_pixmap.m_height;
	t_tex.m_orig_width=t_tex.m_width;
	t_tex.m_orig_height=t_tex.m_height;
	c_TTexture.m_PushBindTexture(t_tex,t_flags);
	return t_tex;
}
c_TTexture.prototype.p_ResizeNoSmooth=function(){
	this.m_resize_smooth=false;
	return 0;
}
c_TTexture.prototype.p_FreeTexture=function(){
	c_TTexture.m_PushBindTexture(this,-255);
	return 0;
}
function c_TSurface(){
	Object.call(this);
	this.m_brush=c_TBrush.m_new.call(new c_TBrush);
	this.m_surf_id=0;
	this.m_no_verts=0;
	this.m_no_tris=0;
	this.m_vbo_dyn=false;
	this.m_tris=c_ShortBuffer.m_new.call(new c_ShortBuffer);
	this.m_vert_data=c_VertexDataBuffer.m_new.call(new c_VertexDataBuffer);
	this.m_reset_vbo=-1;
	this.m_vert_array_size=1;
	this.m_tri_array_size=1;
	this.m_vert_bone1_no=[];
	this.m_vert_weight1=[];
	this.m_vert_bone2_no=[];
	this.m_vert_weight2=[];
	this.m_vert_bone3_no=[];
	this.m_vert_weight3=[];
	this.m_vert_bone4_no=[];
	this.m_vert_weight4=[];
	this.m_vert_anim=[];
	this.m_alpha_enable=false;
	this.m_vbo_id=[0,0,0,0,0,0,0];
	this.m_anim_frame=0;
}
c_TSurface.m_new=function(){
	return this;
}
c_TSurface.prototype.p_TriangleVertex=function(t_tri_no,t_corner){
	return this.m_tris.p_Peek(t_tri_no*3+(2-t_corner));
}
c_TSurface.prototype.p_UpdateNormals=function(t_create_only){
	var t_norm_map=c_NormMap.m_new.call(new c_NormMap);
	for(var t_t=0;t_t<=this.m_no_tris-1;t_t=t_t+1){
		var t_tri_no=(t_t+1)*3;
		var t_v0=this.m_tris.p_Peek(t_tri_no-3);
		var t_v1=this.m_tris.p_Peek(t_tri_no-2);
		var t_v2=this.m_tris.p_Peek(t_tri_no-1);
		var t_ax=this.m_vert_data.p_VertexX(t_v1)-this.m_vert_data.p_VertexX(t_v0);
		var t_ay=this.m_vert_data.p_VertexY(t_v1)-this.m_vert_data.p_VertexY(t_v0);
		var t_az=this.m_vert_data.p_VertexZ(t_v1)-this.m_vert_data.p_VertexZ(t_v0);
		var t_bx=this.m_vert_data.p_VertexX(t_v2)-this.m_vert_data.p_VertexX(t_v1);
		var t_by=this.m_vert_data.p_VertexY(t_v2)-this.m_vert_data.p_VertexY(t_v1);
		var t_bz=this.m_vert_data.p_VertexZ(t_v2)-this.m_vert_data.p_VertexZ(t_v1);
		var t_nx=t_ay*t_bz-t_az*t_by;
		var t_ny=t_az*t_bx-t_ax*t_bz;
		var t_nz=t_ax*t_by-t_ay*t_bx;
		var t_norm=c_Vector.m_new.call(new c_Vector,t_nx,t_ny,t_nz);
		var t_vnorm=null;
		var t_vx=null;
		var t_new_norm=null;
		var t_vhelper=null;
		for(var t_c=0;t_c<=2;t_c=t_c+1){
			var t_v=this.p_TriangleVertex(t_t,t_c);
			t_vx=this.m_vert_data.p_PeekVertCoords(t_v);
			t_vhelper=t_norm_map.p_Get(t_vx);
			if(!((t_vhelper)!=null)){
				t_vhelper=c_NormHelperClass.m_new.call(new c_NormHelperClass);
				t_vhelper.m_vec=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
				t_vhelper.m_vert=t_v;
			}
			t_vhelper.m_vec=t_norm.p_Add(t_vhelper.m_vec);
			if(!t_create_only){
				t_norm_map.p_Set2(t_vx,t_vhelper);
			}
		}
	}
	for(var t_v3=0;t_v3<=this.m_no_verts-1;t_v3=t_v3+1){
		var t_vx2=this.m_vert_data.p_PeekVertCoords(t_v3);
		var t_norm2=t_norm_map.p_Get(t_vx2);
		if((t_norm2)!=null){
			t_norm2.m_vec=t_norm2.m_vec.p_Normalize();
			this.m_vert_data.p_PokeNormals(t_v3,t_norm2.m_vec.m_x,t_norm2.m_vec.m_y,t_norm2.m_vec.m_z);
		}
	}
	this.m_reset_vbo=this.m_reset_vbo|4;
	return 0;
}
c_TSurface.prototype.p_AddVertex=function(t_x,t_y,t_z,t_u,t_v,t_w){
	this.m_no_verts=this.m_no_verts+1;
	if(this.m_no_verts>=this.m_vert_array_size){
		do{
			this.m_vert_array_size=this.m_vert_array_size+512;
		}while(!(this.m_vert_array_size>this.m_no_verts));
		var t_vas=this.m_vert_array_size;
		this.m_vert_data=bb_monkeybuffer_CopyDataBuffer2(this.m_vert_data,c_VertexDataBuffer.m_Create(t_vas));
	}
	var t_vid=this.m_no_verts-1;
	var t_data=[t_x,t_y,-t_z,0.0,1.0,1.0,1.0,0.0,1.0,1.0,1.0,1.0,t_u,t_v,t_u,t_v];
	this.m_vert_data.p_PokeFloatArray(t_vid,t_data,-1);
	return t_vid;
}
c_TSurface.prototype.p_AddVertex2=function(t_data,t_len){
	if(t_len==-1){
		t_len=t_data.length;
	}
	var t_total=t_len>>4;
	var t_vid=this.m_no_verts;
	this.m_no_verts=this.m_no_verts+t_total;
	if(this.m_no_verts>=this.m_vert_array_size){
		do{
			this.m_vert_array_size=this.m_vert_array_size+512;
		}while(!(this.m_vert_array_size>this.m_no_verts));
		var t_vas=this.m_vert_array_size;
		this.m_vert_data=bb_monkeybuffer_CopyDataBuffer2(this.m_vert_data,c_VertexDataBuffer.m_Create(t_vas));
	}
	this.m_vert_data.p_PokeFloatArray(t_vid,t_data,t_len);
	return t_vid+(t_total-1);
}
c_TSurface.prototype.p_AddTriangle=function(t_v0,t_v1,t_v2){
	this.m_no_tris=this.m_no_tris+1;
	if(this.m_no_tris>=this.m_tri_array_size){
		do{
			this.m_tri_array_size=this.m_tri_array_size+512;
		}while(!(this.m_tri_array_size>this.m_no_tris));
		var t_tas=this.m_tri_array_size;
		this.m_tris=bb_monkeybuffer_CopyShortBuffer(this.m_tris,c_ShortBuffer.m_Create(t_tas*3));
	}
	var t_v0i=this.m_no_tris*3-3;
	var t_v1i=this.m_no_tris*3-2;
	var t_v2i=this.m_no_tris*3-1;
	this.m_tris.p_Poke(t_v0i,[t_v2,t_v1,t_v0],-1);
	this.m_reset_vbo=-1;
	return this.m_no_tris;
}
c_TSurface.prototype.p_AddTriangle2=function(t_arr,t_len){
	if(t_len==-1){
		t_len=t_arr.length;
	}
	var t_t0=this.m_no_tris*3;
	this.m_no_tris=this.m_no_tris+((t_len/3)|0);
	if(this.m_no_tris>=this.m_tri_array_size){
		do{
			this.m_tri_array_size=this.m_tri_array_size+512;
		}while(!(this.m_tri_array_size>this.m_no_tris));
		var t_tas=this.m_tri_array_size;
		this.m_tris=bb_monkeybuffer_CopyShortBuffer(this.m_tris,c_ShortBuffer.m_Create(t_tas*3));
	}
	var t_temp=0;
	for(var t_t=0;t_t<=t_len-1;t_t=t_t+3){
		t_temp=t_arr[t_t];
		t_arr[t_t]=t_arr[t_t+2];
		t_arr[t_t+2]=t_temp;
	}
	this.m_tris.p_Poke(t_t0,t_arr,t_len);
	this.m_reset_vbo=-1;
	return this.m_no_tris;
}
c_TSurface.prototype.p_ClearSurface=function(t_clear_verts,t_clear_tris,t_keep_array){
	if(t_clear_verts){
		this.m_no_verts=0;
		if(!t_keep_array){
			this.m_vert_data=c_VertexDataBuffer.m_Create(0);
			this.m_vert_array_size=1;
		}
	}
	if(t_clear_tris){
		this.m_no_tris=0;
		if(!t_keep_array){
			this.m_tris=c_ShortBuffer.m_Create(0);
			this.m_tri_array_size=1;
		}
	}
	this.m_reset_vbo=-1;
	return 0;
}
c_TSurface.prototype.p_VertexTexCoords=function(t_vid,t_u,t_v,t_w,t_coord_set){
	if(t_coord_set==0){
		this.m_vert_data.p_PokeTexCoords0(t_vid,t_u,t_v);
	}else{
		if(t_coord_set==1){
			this.m_vert_data.p_PokeTexCoords1(t_vid,t_u,t_v);
		}
	}
	this.m_reset_vbo=this.m_reset_vbo|2;
	return 0;
}
c_TSurface.prototype.p_CropSurfaceBuffers=function(){
	if(this.m_no_verts<1 && this.m_no_tris<1){
		return 0;
	}
	this.m_vert_data=bb_monkeybuffer_CopyDataBuffer2(this.m_vert_data,c_VertexDataBuffer.m_Create(this.m_no_verts));
	this.m_tris=bb_monkeybuffer_CopyShortBuffer(this.m_tris,c_ShortBuffer.m_Create(this.m_no_tris*3));
	this.m_vert_array_size=this.m_no_verts;
	this.m_tri_array_size=this.m_no_tris;
	return 0;
}
c_TSurface.prototype.p_VertexNormal=function(t_vid,t_nx,t_ny,t_nz){
	this.m_vert_data.p_PokeNormals(t_vid,t_nx,t_ny,-t_nz);
	this.m_reset_vbo=this.m_reset_vbo|4;
	return 0;
}
c_TSurface.prototype.p_VertexColor=function(t_vid,t_r,t_g,t_b,t_a){
	this.m_vert_data.p_PokeColor(t_vid,t_r*0.0039215686274509803,t_g*0.0039215686274509803,t_b*0.0039215686274509803,t_a);
	this.m_reset_vbo=this.m_reset_vbo|8;
	return 0;
}
c_TSurface.prototype.p_PaintSurface=function(t_bru){
	if(this.m_brush==null){
		this.m_brush=c_TBrush.m_new.call(new c_TBrush);
	}
	this.m_brush.m_no_texs=t_bru.m_no_texs;
	this.m_brush.m_name=t_bru.m_name;
	this.m_brush.m_red=t_bru.m_red;
	this.m_brush.m_green=t_bru.m_green;
	this.m_brush.m_blue=t_bru.m_blue;
	this.m_brush.m_alpha=t_bru.m_alpha;
	this.m_brush.m_shine=t_bru.m_shine;
	this.m_brush.m_blend=t_bru.m_blend;
	this.m_brush.m_fx=t_bru.m_fx;
	for(var t_i=0;t_i<=7;t_i=t_i+1){
		this.m_brush.m_tex[t_i]=t_bru.m_tex[t_i];
	}
	return 0;
}
c_TSurface.prototype.p_PaintSurface2=function(t_tex,t_index){
	if(this.m_brush==null){
		this.m_brush=c_TBrush.m_new.call(new c_TBrush);
	}
	this.m_brush.m_no_texs=bb_math_Max(this.m_brush.m_no_texs,t_index+1);
	this.m_brush.m_tex[t_index]=t_tex;
	return 0;
}
function c_List2(){
	Object.call(this);
	this.m__head=(c_HeadNode2.m_new.call(new c_HeadNode2));
}
c_List2.m_new=function(){
	return this;
}
c_List2.prototype.p_AddLast2=function(t_data){
	return c_Node3.m_new.call(new c_Node3,this.m__head,this.m__head.m__pred,t_data);
}
c_List2.m_new2=function(t_data){
	var t_=t_data;
	var t_2=0;
	while(t_2<t_.length){
		var t_t=t_[t_2];
		t_2=t_2+1;
		this.p_AddLast2(t_t);
	}
	return this;
}
c_List2.prototype.p_Clear=function(){
	this.m__head.m__succ=this.m__head;
	this.m__head.m__pred=this.m__head;
	return 0;
}
c_List2.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator6.m_new.call(new c_Enumerator6,this);
}
function c_Node3(){
	Object.call(this);
	this.m__succ=null;
	this.m__pred=null;
	this.m__data=null;
}
c_Node3.m_new=function(t_succ,t_pred,t_data){
	this.m__succ=t_succ;
	this.m__pred=t_pred;
	this.m__succ.m__pred=this;
	this.m__pred.m__succ=this;
	this.m__data=t_data;
	return this;
}
c_Node3.m_new2=function(){
	return this;
}
function c_HeadNode2(){
	c_Node3.call(this);
}
c_HeadNode2.prototype=extend_class(c_Node3);
c_HeadNode2.m_new=function(){
	c_Node3.m_new2.call(this);
	this.m__succ=(this);
	this.m__pred=(this);
	return this;
}
function c_TColTree(){
	Object.call(this);
	this.m_reset_col_tree=0;
	this.m_divSphere_p=new_object_array(1);
	this.m_divSphere_r=new_number_array(1);
	this.m_collider_tree=null;
}
c_TColTree.m_new=function(){
	return this;
}
c_TColTree.prototype.p_CreateMeshTree=function(t_mesh,t_max_tris){
	if(this.m_reset_col_tree==1){
		if(this.m_collider_tree!=null){
			this.m_collider_tree=null;
		}
		this.m_reset_col_tree=0;
	}
	if(this.m_collider_tree==null){
		var t_total_verts_count=0;
		var t_vindex=0;
		var t_triindex=1;
		var t_total_tris=0;
		var t_total_verts=0;
		var t_=t_mesh.m_surf_list.p_ObjectEnumerator();
		while(t_.p_HasNext()){
			var t_surf=t_.p_NextObject();
			t_total_tris+=t_surf.m_no_tris;
			t_total_verts+=t_surf.m_no_verts;
		}
		this.m_collider_tree=c_MeshCollider.m_new.call(new c_MeshCollider,t_total_verts,t_total_tris+1);
		var t_s=0;
		var t_temp_vec0=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
		var t_temp_vec1=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
		var t_temp_vec2=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
		var t_2=t_mesh.m_surf_list.p_ObjectEnumerator();
		while(t_2.p_HasNext()){
			var t_surf2=t_2.p_NextObject();
			t_s+=1;
			var t_no_tris=t_surf2.m_no_tris;
			var t_no_verts=t_surf2.m_no_verts;
			if(t_no_tris!=0){
				for(var t_i=0;t_i<=t_no_tris-1;t_i=t_i+1){
					var t_i3=t_i*3;
					var t_v0=t_surf2.m_tris.p_Peek(t_i3+0);
					var t_v1=t_surf2.m_tris.p_Peek(t_i3+1);
					var t_v2=t_surf2.m_tris.p_Peek(t_i3+2);
					var t_ti=t_triindex*3;
					this.m_collider_tree.m_tri_vix[t_ti+0]=t_v2+t_total_verts_count;
					this.m_collider_tree.m_tri_vix[t_ti+1]=t_v1+t_total_verts_count;
					this.m_collider_tree.m_tri_vix[t_ti+2]=t_v0+t_total_verts_count;
					t_surf2.m_vert_data.p_GetVertCoords(t_temp_vec0,t_v0);
					t_temp_vec0.m_z=-t_temp_vec0.m_z;
					t_surf2.m_vert_data.p_GetVertCoords(t_temp_vec1,t_v1);
					t_temp_vec1.m_z=-t_temp_vec1.m_z;
					t_surf2.m_vert_data.p_GetVertCoords(t_temp_vec2,t_v2);
					t_temp_vec2.m_z=-t_temp_vec2.m_z;
					this.m_collider_tree.m_tri_verts[t_v0+t_total_verts_count]=t_temp_vec0.p_Copy();
					this.m_collider_tree.m_tri_verts[t_v1+t_total_verts_count]=t_temp_vec1.p_Copy();
					this.m_collider_tree.m_tri_verts[t_v2+t_total_verts_count]=t_temp_vec2.p_Copy();
					this.m_collider_tree.m_tri_centres[t_triindex].m_x=(t_temp_vec0.m_x+t_temp_vec1.m_x+t_temp_vec2.m_x)*0.33333333333333331;
					this.m_collider_tree.m_tri_centres[t_triindex].m_y=(t_temp_vec0.m_y+t_temp_vec1.m_y+t_temp_vec2.m_y)*0.33333333333333331;
					this.m_collider_tree.m_tri_centres[t_triindex].m_z=(t_temp_vec0.m_z+t_temp_vec1.m_z+t_temp_vec2.m_z)*0.33333333333333331;
					this.m_collider_tree.m_tris[t_triindex]=t_triindex;
					this.m_collider_tree.m_tri_surface[t_triindex]=t_s;
					this.m_collider_tree.m_tri_number[t_triindex]=t_i;
					t_vindex+=3;
					t_triindex+=1;
				}
				t_total_verts_count+=t_no_verts;
			}
		}
		this.m_collider_tree.m_tree=this.m_collider_tree.p_CreateNode(this.m_collider_tree.m_tris,0,t_max_tris);
		this.m_collider_tree.m_tree.m_name=t_mesh.m_classname;
	}
	return this.m_collider_tree;
}
function c_MojoSurface(){
	EmptyNullClass.call(this);
	this.m_tex=null;
	this.m_path="";
	this.m_loaded=0;
	this.m_xstep=.0;
	this.m_ystep=.0;
}
c_MojoSurface.prototype=extend_class(EmptyNullClass);
c_MojoSurface.m_new=function(){
	return this;
}
c_MojoSurface.m_Create=function(t_path){
	var t_s=c_MojoSurface.m_new.call(new c_MojoSurface);
	t_s.m_tex=bb_functions_CreateTexture(0,0,1,1);
	if(t_path!=""){
		t_s.m_path=t_path;
	}
	return t_s;
}
c_MojoSurface.m_list=[];
c_MojoSurface.m_isLoading=false;
c_MojoSurface.prototype.Width=function(){
	return this.m_tex.m_orig_width;
}
c_MojoSurface.prototype.Height=function(){
	return this.m_tex.m_orig_height;
}
c_MojoSurface.prototype.p_LoadTexture=function(t_path,t_mesh,t_device){
	this.m_tex.p_ResizeNoSmooth();
	this.m_tex.m_pixmap.p_ClearBind();
	c_TTexture.m_LoadAnimTexture(t_path,3,0,0,0,1,this.m_tex,1);
	this.m_loaded=1;
	this.m_xstep=1.0/(this.Width());
	this.m_ystep=1.0/(this.Height());
	return this.m_tex!=null;
}
c_MojoSurface.m_PreLoad=function(t_path,t_mesh,t_device){
	var t_s=c_MojoSurface.m_Create(t_path);
	var t_sz=c_MojoSurface.m_list.length;
	c_MojoSurface.m_list=resize_string_array(c_MojoSurface.m_list,t_sz+1);
	c_MojoSurface.m_list[t_sz]=t_path;
	c_MojoSurface.m_isLoading=true;
	t_s.p_LoadTexture(t_path,t_mesh,t_device);
	return t_s;
}
c_MojoSurface.prototype.Discard=function(){
	if((this.m_tex)!=null){
		this.m_tex.p_FreeTexture();
	}
	this.m_tex=null;
	return 0;
}
c_MojoSurface.prototype.Loaded=function(){
	return this.m_loaded;
}
function c_List3(){
	Object.call(this);
	this.m__head=(c_HeadNode3.m_new.call(new c_HeadNode3));
}
c_List3.m_new=function(){
	return this;
}
c_List3.prototype.p_AddLast3=function(t_data){
	return c_Node4.m_new.call(new c_Node4,this.m__head,this.m__head.m__pred,t_data);
}
c_List3.m_new2=function(t_data){
	var t_=t_data;
	var t_2=0;
	while(t_2<t_.length){
		var t_t=t_[t_2];
		t_2=t_2+1;
		this.p_AddLast3(t_t);
	}
	return this;
}
c_List3.prototype.p_Clear=function(){
	this.m__head.m__succ=this.m__head;
	this.m__head.m__pred=this.m__head;
	return 0;
}
c_List3.prototype.p_Compare2=function(t_lhs,t_rhs){
	error("Unable to compare items");
	return 0;
}
c_List3.prototype.p_Sort=function(t_ascending){
	var t_ccsgn=-1;
	if((t_ascending)!=0){
		t_ccsgn=1;
	}
	var t_insize=1;
	do{
		var t_merges=0;
		var t_tail=this.m__head;
		var t_p=this.m__head.m__succ;
		while(t_p!=this.m__head){
			t_merges+=1;
			var t_q=t_p.m__succ;
			var t_qsize=t_insize;
			var t_psize=1;
			while(t_psize<t_insize && t_q!=this.m__head){
				t_psize+=1;
				t_q=t_q.m__succ;
			}
			do{
				var t_t=null;
				if(((t_psize)!=0) && ((t_qsize)!=0) && t_q!=this.m__head){
					var t_cc=this.p_Compare2(t_p.m__data,t_q.m__data)*t_ccsgn;
					if(t_cc<=0){
						t_t=t_p;
						t_p=t_p.m__succ;
						t_psize-=1;
					}else{
						t_t=t_q;
						t_q=t_q.m__succ;
						t_qsize-=1;
					}
				}else{
					if((t_psize)!=0){
						t_t=t_p;
						t_p=t_p.m__succ;
						t_psize-=1;
					}else{
						if(((t_qsize)!=0) && t_q!=this.m__head){
							t_t=t_q;
							t_q=t_q.m__succ;
							t_qsize-=1;
						}else{
							break;
						}
					}
				}
				t_t.m__pred=t_tail;
				t_tail.m__succ=t_t;
				t_tail=t_t;
			}while(!(false));
			t_p=t_q;
		}
		t_tail.m__succ=this.m__head;
		this.m__head.m__pred=t_tail;
		if(t_merges<=1){
			return 0;
		}
		t_insize*=2;
	}while(!(false));
}
c_List3.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator14.m_new.call(new c_Enumerator14,this);
}
c_List3.prototype.p_IsEmpty=function(){
	return this.m__head.m__succ==this.m__head;
}
function c_Node4(){
	Object.call(this);
	this.m__succ=null;
	this.m__pred=null;
	this.m__data=null;
}
c_Node4.m_new=function(t_succ,t_pred,t_data){
	this.m__succ=t_succ;
	this.m__pred=t_pred;
	this.m__succ.m__pred=this;
	this.m__pred.m__succ=this;
	this.m__data=t_data;
	return this;
}
c_Node4.m_new2=function(){
	return this;
}
function c_HeadNode3(){
	c_Node4.call(this);
}
c_HeadNode3.prototype=extend_class(c_Node4);
c_HeadNode3.m_new=function(){
	c_Node4.m_new2.call(this);
	this.m__succ=(this);
	this.m__pred=(this);
	return this;
}
function c_TPixmap(){
	Object.call(this);
	this.m_width=0;
	this.m_height=0;
	this.m_bind=0;
}
c_TPixmap.m_preloader=null;
c_TPixmap.m_PreLoadPixmap=function(t_file){
	return c_TPixmap.m_preloader.p_PreLoad(t_file);
}
c_TPixmap.m_manager=null;
c_TPixmap.m_LoadPixmap=function(t_f){
	return c_TPixmap.m_manager.p_LoadPixmap(t_f);
}
c_TPixmap.prototype.p_ResizePixmapNoSmooth=function(t_neww,t_newh){
}
c_TPixmap.prototype.p_Copy=function(){
	return this.p_ResizePixmapNoSmooth(this.m_width,this.m_height);
}
c_TPixmap.prototype.p_MaskPixmap=function(t_r,t_g,t_b){
}
c_TPixmap.prototype.p_ResizePixmap=function(t_neww,t_newh){
}
c_TPixmap.prototype.p_DecBind=function(){
	this.m_bind-=1;
	if(this.m_bind<0){
		this.m_bind=0;
	}
}
c_TPixmap.prototype.p_GetBindCount=function(){
	return this.m_bind;
}
c_TPixmap.prototype.p_FreePixmap=function(){
}
c_TPixmap.m_new=function(){
	return this;
}
c_TPixmap.m_CreatePixmap=function(t_w,t_h,t_format){
	return c_TPixmap.m_manager.p_CreatePixmap(t_w,t_h,t_format);
}
c_TPixmap.prototype.p_SetBind=function(){
	this.m_bind+=1;
}
c_TPixmap.prototype.p_ClearBind=function(){
	this.m_bind=0;
}
function c_TPixmapPreloader(){
	Object.call(this);
	this.m_manager=null;
	this.m_old_file=new_string_array(1);
	this.m_start_stack=c_Stack2.m_new.call(new c_Stack2);
	this.m_finish_stack=c_Stack2.m_new.call(new c_Stack2);
	this.m_total=0;
	this.m_loading=false;
}
c_TPixmapPreloader.prototype.p_CheckAllLoaded=function(){
	if(this.m_start_stack.p_IsEmpty()){
		this.m_loading=false;
		return 1;
	}
	return 0;
}
c_TPixmapPreloader.prototype.p_PreLoad=function(t_file){
	if(!((this.m_manager)!=null)){
		error("**ERROR: no preload manager");
	}
	if(t_file[0]!=this.m_old_file[0] || t_file[t_file.length-1]!=this.m_old_file[this.m_old_file.length-1]){
		var t_=t_file;
		var t_2=0;
		while(t_2<t_.length){
			var t_f=t_[t_2];
			t_2=t_2+1;
			if(t_f==""){
				continue;
			}
			var t_skip=false;
			var t_3=this.m_start_stack.p_ObjectEnumerator();
			while(t_3.p_HasNext()){
				var t_ss=t_3.p_NextObject();
				if(t_ss.m_file==t_f){
					t_skip=true;
					break;
				}
			}
			var t_4=this.m_finish_stack.p_ObjectEnumerator();
			while(t_4.p_HasNext()){
				var t_ss2=t_4.p_NextObject();
				if(t_ss2.m_file==t_f){
					t_skip=true;
					break;
				}
			}
			if(t_skip!=true){
				this.m_total+=1;
				this.m_start_stack.p_Insert2(0,c_PixmapStack.m_new.call(new c_PixmapStack,t_f,bb_data_FixDataPath(t_f),this.m_total));
			}
		}
		this.m_old_file=t_file;
	}
	if(!this.m_start_stack.p_IsEmpty()){
		this.m_loading=true;
		var t_f2=null;
		var t_5=this.m_start_stack.p_Backwards().p_ObjectEnumerator();
		while(t_5.p_HasNext()){
			t_f2=t_5.p_NextObject();
			if(t_f2.m_loading==false){
				this.m_manager.p_PreLoadData(t_f2.m_file,t_f2.m_id);
				t_f2.m_loading=true;
				break;
			}else{
				if(this.m_manager.p_IsLoaded(t_f2.m_id)){
					this.m_start_stack.p_RemoveEach(t_f2);
					this.m_finish_stack.p_Push4(t_f2);
				}
			}
		}
	}
	return this.p_CheckAllLoaded();
}
c_TPixmapPreloader.prototype.p_RemoveFromStack=function(t_file){
	if(!this.m_finish_stack.p_IsEmpty()){
		var t_j=0;
		var t_=this.m_finish_stack.p_ObjectEnumerator();
		while(t_.p_HasNext()){
			var t_i=t_.p_NextObject();
			t_j=t_j+1;
			if(t_i.m_file==t_file || t_i.m_new_file==t_file){
				this.m_finish_stack.p_Remove2(t_j);
				break;
			}
		}
	}
}
c_TPixmapPreloader.m_new=function(t_m){
	this.m_manager=t_m;
	return this;
}
c_TPixmapPreloader.m_new2=function(){
	return this;
}
c_TPixmapPreloader.prototype.p_GetID=function(t_file){
	if(!this.m_finish_stack.p_IsEmpty()){
		var t_=this.m_finish_stack.p_ObjectEnumerator();
		while(t_.p_HasNext()){
			var t_i=t_.p_NextObject();
			if(t_i.m_file==t_file || t_i.m_new_file==t_file){
				return t_i.m_id;
			}
		}
	}
	return 0;
}
c_TPixmapPreloader.prototype.p_GetPixmapPreLoad=function(t_p,t_file){
	var t_id=this.p_GetID(t_file);
	this.m_manager.p_SetPixmapFromID(t_p,t_id,t_file);
}
function c_PixmapStack(){
	Object.call(this);
	this.m_file="";
	this.m_new_file="";
	this.m_id=0;
	this.m_loading=false;
}
c_PixmapStack.m_new=function(t_f,t_nf,t_i){
	this.m_file=t_f;
	this.m_new_file=t_nf;
	this.m_id=t_i;
	return this;
}
c_PixmapStack.m_new2=function(){
	return this;
}
function c_Stack2(){
	Object.call(this);
	this.m_data=[];
	this.m_length=0;
}
c_Stack2.m_new=function(){
	return this;
}
c_Stack2.m_new2=function(t_data){
	this.m_data=t_data.slice(0);
	this.m_length=t_data.length;
	return this;
}
c_Stack2.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator.m_new.call(new c_Enumerator,this);
}
c_Stack2.m_NIL=null;
c_Stack2.prototype.p_Length2=function(t_newlength){
	if(t_newlength<this.m_length){
		for(var t_i=t_newlength;t_i<this.m_length;t_i=t_i+1){
			this.m_data[t_i]=c_Stack2.m_NIL;
		}
	}else{
		if(t_newlength>this.m_data.length){
			this.m_data=resize_object_array(this.m_data,bb_math_Max(this.m_length*2+10,t_newlength));
		}
	}
	this.m_length=t_newlength;
}
c_Stack2.prototype.p_Length=function(){
	return this.m_length;
}
c_Stack2.prototype.p_Insert2=function(t_index,t_value){
	if(this.m_length==this.m_data.length){
		this.m_data=resize_object_array(this.m_data,this.m_length*2+10);
	}
	for(var t_i=this.m_length;t_i>t_index;t_i=t_i+-1){
		this.m_data[t_i]=this.m_data[t_i-1];
	}
	this.m_data[t_index]=t_value;
	this.m_length+=1;
}
c_Stack2.prototype.p_IsEmpty=function(){
	return this.m_length==0;
}
c_Stack2.prototype.p_Backwards=function(){
	return c_BackwardsStack.m_new.call(new c_BackwardsStack,this);
}
c_Stack2.prototype.p_Equals=function(t_lhs,t_rhs){
	return t_lhs==t_rhs;
}
c_Stack2.prototype.p_RemoveEach=function(t_value){
	var t_i=0;
	var t_j=this.m_length;
	while(t_i<this.m_length){
		if(!this.p_Equals(this.m_data[t_i],t_value)){
			t_i+=1;
			continue;
		}
		var t_b=t_i;
		var t_e=t_i+1;
		while(t_e<this.m_length && this.p_Equals(this.m_data[t_e],t_value)){
			t_e+=1;
		}
		while(t_e<this.m_length){
			this.m_data[t_b]=this.m_data[t_e];
			t_b+=1;
			t_e+=1;
		}
		this.m_length-=t_e-t_b;
		t_i+=1;
	}
	t_i=this.m_length;
	while(t_i<t_j){
		this.m_data[t_i]=c_Stack2.m_NIL;
		t_i+=1;
	}
}
c_Stack2.prototype.p_Push4=function(t_value){
	if(this.m_length==this.m_data.length){
		this.m_data=resize_object_array(this.m_data,this.m_length*2+10);
	}
	this.m_data[this.m_length]=t_value;
	this.m_length+=1;
}
c_Stack2.prototype.p_Push5=function(t_values,t_offset,t_count){
	for(var t_i=0;t_i<t_count;t_i=t_i+1){
		this.p_Push4(t_values[t_offset+t_i]);
	}
}
c_Stack2.prototype.p_Push6=function(t_values,t_offset){
	this.p_Push5(t_values,t_offset,t_values.length-t_offset);
}
c_Stack2.prototype.p_Remove2=function(t_index){
	for(var t_i=t_index;t_i<this.m_length-1;t_i=t_i+1){
		this.m_data[t_i]=this.m_data[t_i+1];
	}
	this.m_length-=1;
	this.m_data[this.m_length]=c_Stack2.m_NIL;
}
function c_Enumerator(){
	Object.call(this);
	this.m_stack=null;
	this.m_index=0;
}
c_Enumerator.m_new=function(t_stack){
	this.m_stack=t_stack;
	return this;
}
c_Enumerator.m_new2=function(){
	return this;
}
c_Enumerator.prototype.p_HasNext=function(){
	return this.m_index<this.m_stack.p_Length();
}
c_Enumerator.prototype.p_NextObject=function(){
	this.m_index+=1;
	return this.m_stack.m_data[this.m_index-1];
}
function bb_math_Max(t_x,t_y){
	if(t_x>t_y){
		return t_x;
	}
	return t_y;
}
function bb_math_Max2(t_x,t_y){
	if(t_x>t_y){
		return t_x;
	}
	return t_y;
}
function c_BackwardsStack(){
	Object.call(this);
	this.m_stack=null;
}
c_BackwardsStack.m_new=function(t_stack){
	this.m_stack=t_stack;
	return this;
}
c_BackwardsStack.m_new2=function(){
	return this;
}
c_BackwardsStack.prototype.p_ObjectEnumerator=function(){
	return c_BackwardsEnumerator.m_new.call(new c_BackwardsEnumerator,this.m_stack);
}
function c_BackwardsEnumerator(){
	Object.call(this);
	this.m_stack=null;
	this.m_index=0;
}
c_BackwardsEnumerator.m_new=function(t_stack){
	this.m_stack=t_stack;
	this.m_index=t_stack.m_length;
	return this;
}
c_BackwardsEnumerator.m_new2=function(){
	return this;
}
c_BackwardsEnumerator.prototype.p_HasNext=function(){
	return this.m_index>0;
}
c_BackwardsEnumerator.prototype.p_NextObject=function(){
	this.m_index-=1;
	return this.m_stack.m_data[this.m_index];
}
function bb_functions_PreLoadPixmap(t_fs){
	return c_TPixmap.m_PreLoadPixmap(t_fs);
}
function bb_functions_PreLoadPixmap2(t_fs){
	return c_TPixmap.m_PreLoadPixmap([t_fs]);
}
function bb_mojographics_SetMojoEmulation(){
	c_MojoEmulationDevice.m_SetDevice();
}
function bb_opengles20_SetRender(t_flags){
	c_TRender.m_render=(c_OpenglES20.m_new.call(new c_OpenglES20));
	c_TRender.m_render.p_GraphicsInit(t_flags);
	bb_mojographics_SetMojoEmulation();
	return 0;
}
var bb_app__updateRate=0;
function bb_app_SetUpdateRate(t_hertz){
	bb_app__updateRate=t_hertz;
	bb_app__game.SetUpdateRate(t_hertz);
}
function bb_input_KeyHit(t_key){
	return bb_input_device.p_KeyHit(t_key);
}
function c_DeltaTimer(){
	Object.call(this);
	this.m_targetfps=60.0;
	this.m_lastticks=.0;
	this.m_currentticks=.0;
	this.m_frametime=.0;
	this.m_delta=.0;
}
c_DeltaTimer.m_new=function(t_fps){
	this.m_targetfps=t_fps;
	this.m_lastticks=(bb_app_Millisecs());
	return this;
}
c_DeltaTimer.m_new2=function(){
	return this;
}
c_DeltaTimer.prototype.p_UpdateDelta=function(){
	this.m_currentticks=(bb_app_Millisecs());
	this.m_frametime=this.m_currentticks-this.m_lastticks;
	this.m_delta=this.m_frametime/(1000.0/this.m_targetfps);
	this.m_lastticks=this.m_currentticks;
}
function bb_app_Millisecs(){
	return bb_app__game.Millisecs();
}
function c_TCamera(){
	c_TEntity.call(this);
	this.m_frustum=[];
	this.m_vx=0;
	this.m_vy=0;
	this.m_vwidth=0;
	this.m_vheight=0;
	this.m_zoom=1.0;
	this.m_inv_zoom=1.0;
	this.m_fov_y=.0;
	this.m_aspect=.0;
	this.m_viewport=new_number_array(4);
	this.m_cam_link=null;
	this.m_cls_r=0.0;
	this.m_cls_g=0.0;
	this.m_cls_b=0.0;
	this.m_range_near=1.0;
	this.m_range_far=1000.0;
	this.m_eyedx=0.0;
	this.m_focus=1.0;
	this.m_eyedy=0.0;
	this.m_proj_mode=1;
	this.m_proj_mat=c_Matrix.m_new.call(new c_Matrix);
	this.m_draw2D=0;
	this.m_oldw=0;
	this.m_oldh=0;
	this.m_mod_mat=c_Matrix.m_new.call(new c_Matrix);
	this.m_view_mat=c_Matrix.m_new.call(new c_Matrix);
	this.m_projview_mat=c_Matrix.m_new.call(new c_Matrix);
	this.m_cls_color=true;
	this.m_cls_zbuffer=true;
	this.m_fog_mode=0;
	this.m_fog_r=.0;
	this.m_fog_g=.0;
	this.m_fog_b=.0;
	this.m_fog_range_near=1.0;
	this.m_fog_range_far=1000.0;
}
c_TCamera.prototype=extend_class(c_TEntity);
c_TCamera.m_new=function(){
	c_TEntity.m_new.call(this);
	this.m_frustum=bb_monkeyutility_AllocateFloatArray(6,4);
	return this;
}
c_TCamera.prototype.p_CameraZoom=function(t_zoom_val){
	this.m_zoom=t_zoom_val;
	this.m_inv_zoom=1.0/t_zoom_val;
	this.m_fov_y=(Math.atan(1.0/(this.m_zoom*((this.m_vwidth)/(this.m_vheight))))*R2D)*2.0;
	return 0;
}
c_TCamera.prototype.p_CameraViewport=function(t_x,t_y,t_w,t_h){
	this.m_vx=t_x;
	this.m_vy=c_TRender.m_height-t_h-t_y;
	this.m_vwidth=t_w;
	this.m_vheight=t_h;
	this.p_CameraZoom(this.m_zoom);
	this.m_aspect=(this.m_vwidth)/(this.m_vheight);
	this.m_viewport[0]=this.m_vx;
	this.m_viewport[1]=this.m_vy;
	this.m_viewport[2]=t_w;
	this.m_viewport[3]=t_h;
	return 0;
}
c_TCamera.m_cam_list=null;
c_TCamera.m_CreateCamera=function(t_parent_ent){
	var t_cam=c_TCamera.m_new.call(new c_TCamera);
	t_cam.p_CameraViewport(0,0,c_TRender.m_width,c_TRender.m_height);
	t_cam.m_classname="Camera";
	t_cam.m_name="proj"+String(c_TCamera.m_cam_list.p_Count());
	t_cam.p_AddParent(t_parent_ent);
	t_cam.m_entity_link=c_TEntity.m_entity_list.p_EntityListAdd(t_cam);
	t_cam.m_cam_link=c_TCamera.m_cam_list.p_EntityListAdd2(t_cam);
	if(t_cam.m_parent!=null){
		t_cam.m_mat.p_Overwrite(t_cam.m_parent.m_mat);
		t_cam.p_UpdateMat(false);
	}else{
		t_cam.p_UpdateMat(true);
	}
	return t_cam;
}
c_TCamera.prototype.p_CameraClsColor=function(t_r,t_g,t_b){
	this.m_cls_r=t_r/255.0;
	this.m_cls_g=t_g/255.0;
	this.m_cls_b=t_b/255.0;
	return 0;
}
c_TCamera.prototype.p_SetPixelCamera=function(){
	if(((this.m_draw2D)!=0) && this.m_oldw==this.m_vwidth && this.m_oldh==this.m_vheight){
		return 0;
	}
	this.m_oldw=this.m_vwidth;
	this.m_oldh=this.m_vheight;
	this.m_draw2D=1;
	if(this.m_name==""){
		this.m_name="pixel";
	}
	var t_left=(this.m_vx)-(this.m_vwidth)*0.5;
	var t_right=(this.m_vwidth)+t_left;
	var t_bottom=(this.m_vy)-(this.m_vheight)/2.0;
	var t_top=(this.m_vheight)+t_bottom;
	var t_near=1.0;
	var t_far=2.0;
	var t_tx=-(t_right+t_left)/(t_right-t_left);
	var t_ty=-(t_top+t_bottom)/(t_top-t_bottom);
	var t_tz=-(t_far+t_near)/(t_far-t_near);
	this.m_proj_mat.m_grid[0][0]=2.0/(t_right-t_left);
	this.m_proj_mat.m_grid[0][1]=0.0;
	this.m_proj_mat.m_grid[0][2]=0.0;
	this.m_proj_mat.m_grid[0][3]=0.0;
	this.m_proj_mat.m_grid[1][0]=0.0;
	this.m_proj_mat.m_grid[1][1]=2.0/(t_top-t_bottom);
	this.m_proj_mat.m_grid[1][2]=0.0;
	this.m_proj_mat.m_grid[1][3]=0.0;
	this.m_proj_mat.m_grid[2][0]=0.0;
	this.m_proj_mat.m_grid[2][1]=0.0;
	this.m_proj_mat.m_grid[2][2]=-2.0/(t_far-t_near);
	this.m_proj_mat.m_grid[2][3]=0.0;
	this.m_proj_mat.m_grid[3][0]=t_tx-1.0;
	this.m_proj_mat.m_grid[3][1]=t_ty+1.0;
	this.m_proj_mat.m_grid[3][2]=t_tz;
	this.m_proj_mat.m_grid[3][3]=1.0;
	this.m_mod_mat.p_LoadIdentity();
	this.m_mat.p_LoadIdentity();
	this.m_view_mat=this.m_mod_mat;
	this.m_projview_mat.p_Overwrite(this.m_proj_mat);
	return 0;
}
c_TCamera.prototype.p_accFrustum=function(t_left_,t_right_,t_bottom,t_top,t_zNear,t_zFar,t_pixdx,t_pixdy){
	var t_xwsize=.0;
	var t_ywsize=.0;
	var t_dx=.0;
	var t_dy=.0;
	if(((t_pixdx)!=0.0) || ((t_pixdy)!=0.0)){
		t_xwsize=t_right_-t_left_;
		t_ywsize=t_top-t_bottom;
		t_dx=-(t_pixdx*t_xwsize/(this.m_viewport[2])+this.m_eyedx*t_zNear/this.m_focus);
		t_dy=-(t_pixdy*t_ywsize/(this.m_viewport[3])+this.m_eyedy*t_zNear/this.m_focus);
	}
	if(this.m_proj_mode==1){
		if(this.m_name==""){
			this.m_name="perspective";
		}
		t_left_+=t_dx;
		t_right_+=t_dx;
		t_bottom+=t_dy;
		t_top+=t_dy;
		this.m_proj_mat.m_grid[0][0]=2.0*t_zNear/(t_right_-t_left_);
		this.m_proj_mat.m_grid[1][0]=0.0;
		this.m_proj_mat.m_grid[2][0]=(t_right_+t_left_)/(t_right_-t_left_);
		this.m_proj_mat.m_grid[3][0]=0.0;
		this.m_proj_mat.m_grid[0][1]=0.0;
		this.m_proj_mat.m_grid[1][1]=2.0*t_zNear/(t_top-t_bottom);
		this.m_proj_mat.m_grid[2][1]=(t_top+t_bottom)/(t_top-t_bottom);
		this.m_proj_mat.m_grid[3][1]=0.0;
		this.m_proj_mat.m_grid[0][2]=0.0;
		this.m_proj_mat.m_grid[1][2]=0.0;
		this.m_proj_mat.m_grid[2][2]=-(t_zFar+t_zNear)/(t_zFar-t_zNear);
		this.m_proj_mat.m_grid[3][2]=-(2.0*t_zFar*t_zNear)/(t_zFar-t_zNear);
		this.m_proj_mat.m_grid[0][3]=0.0;
		this.m_proj_mat.m_grid[1][3]=0.0;
		this.m_proj_mat.m_grid[2][3]=-1.0;
		this.m_proj_mat.m_grid[3][3]=0.0;
	}else{
		if(this.m_proj_mode==2){
			if(this.m_name==""){
				this.m_name="isometric";
			}
			t_left_+=t_dx;
			t_right_+=t_dx;
			t_bottom+=t_dy;
			t_top+=t_dy;
			this.m_proj_mat.m_grid[0][0]=2.0/(t_right_-t_left_);
			this.m_proj_mat.m_grid[0][1]=0.0;
			this.m_proj_mat.m_grid[0][2]=0.0;
			this.m_proj_mat.m_grid[0][3]=0.0;
			this.m_proj_mat.m_grid[1][0]=0.0;
			this.m_proj_mat.m_grid[1][1]=2.0/(t_top-t_bottom);
			this.m_proj_mat.m_grid[1][2]=0.0;
			this.m_proj_mat.m_grid[1][3]=0.0;
			this.m_proj_mat.m_grid[2][0]=0.0;
			this.m_proj_mat.m_grid[2][1]=0.0;
			this.m_proj_mat.m_grid[2][2]=-2.0/(t_zFar-t_zNear);
			this.m_proj_mat.m_grid[2][3]=0.0;
			this.m_proj_mat.m_grid[3][0]=-(t_right_+t_left_)/(t_right_-t_left_);
			this.m_proj_mat.m_grid[3][1]=-(t_top+t_bottom)/(t_top-t_bottom);
			this.m_proj_mat.m_grid[3][2]=-(t_zFar+t_zNear)/(t_zFar-t_zNear);
			this.m_proj_mat.m_grid[3][3]=1.0;
		}else{
			if(this.m_proj_mode==3){
				this.p_SetPixelCamera();
			}
		}
	}
	return 0;
}
c_TCamera.prototype.p_accPerspective=function(t_fovy,t_zNear,t_zFar,t_pixdx,t_pixdy){
	var t_fov2=.0;
	var t_left_=.0;
	var t_right_=.0;
	var t_bottom=.0;
	var t_top=.0;
	t_fov2=t_fovy*0.5;
	t_top=t_zNear/(Math.cos((t_fov2)*D2R)/Math.sin((t_fov2)*D2R));
	t_bottom=-t_top;
	t_right_=t_top*this.m_aspect;
	t_left_=-t_right_;
	this.p_accFrustum(t_left_,t_right_,t_bottom,t_top,t_zNear,t_zFar,t_pixdx,t_pixdy);
	return 0;
}
c_TCamera.prototype.p_ExtractFrustum=function(){
	var t_clip=this.m_projview_mat.p_ToArray();
	var t_t=.0;
	this.m_frustum[0][0]=t_clip[3]-t_clip[0];
	this.m_frustum[0][1]=t_clip[7]-t_clip[4];
	this.m_frustum[0][2]=t_clip[11]-t_clip[8];
	this.m_frustum[0][3]=t_clip[15]-t_clip[12];
	t_t=1.0/Math.sqrt(this.m_frustum[0][0]*this.m_frustum[0][0]+this.m_frustum[0][1]*this.m_frustum[0][1]+this.m_frustum[0][2]*this.m_frustum[0][2]);
	this.m_frustum[0][0]*=t_t;
	this.m_frustum[0][1]*=t_t;
	this.m_frustum[0][2]*=t_t;
	this.m_frustum[0][3]*=t_t;
	this.m_frustum[1][0]=t_clip[3]+t_clip[0];
	this.m_frustum[1][1]=t_clip[7]+t_clip[4];
	this.m_frustum[1][2]=t_clip[11]+t_clip[8];
	this.m_frustum[1][3]=t_clip[15]+t_clip[12];
	t_t=1.0/Math.sqrt(this.m_frustum[1][0]*this.m_frustum[1][0]+this.m_frustum[1][1]*this.m_frustum[1][1]+this.m_frustum[1][2]*this.m_frustum[1][2]);
	this.m_frustum[1][0]*=t_t;
	this.m_frustum[1][1]*=t_t;
	this.m_frustum[1][2]*=t_t;
	this.m_frustum[1][3]*=t_t;
	this.m_frustum[2][0]=t_clip[3]+t_clip[1];
	this.m_frustum[2][1]=t_clip[7]+t_clip[5];
	this.m_frustum[2][2]=t_clip[11]+t_clip[9];
	this.m_frustum[2][3]=t_clip[15]+t_clip[13];
	t_t=1.0/Math.sqrt(this.m_frustum[2][0]*this.m_frustum[2][0]+this.m_frustum[2][1]*this.m_frustum[2][1]+this.m_frustum[2][2]*this.m_frustum[2][2]);
	this.m_frustum[2][0]*=t_t;
	this.m_frustum[2][1]*=t_t;
	this.m_frustum[2][2]*=t_t;
	this.m_frustum[2][3]*=t_t;
	this.m_frustum[3][0]=t_clip[3]-t_clip[1];
	this.m_frustum[3][1]=t_clip[7]-t_clip[5];
	this.m_frustum[3][2]=t_clip[11]-t_clip[9];
	this.m_frustum[3][3]=t_clip[15]-t_clip[13];
	t_t=1.0/Math.sqrt(this.m_frustum[3][0]*this.m_frustum[3][0]+this.m_frustum[3][1]*this.m_frustum[3][1]+this.m_frustum[3][2]*this.m_frustum[3][2]);
	this.m_frustum[3][0]*=t_t;
	this.m_frustum[3][1]*=t_t;
	this.m_frustum[3][2]*=t_t;
	this.m_frustum[3][3]*=t_t;
	this.m_frustum[4][0]=t_clip[3]-t_clip[2];
	this.m_frustum[4][1]=t_clip[7]-t_clip[6];
	this.m_frustum[4][2]=t_clip[11]-t_clip[10];
	this.m_frustum[4][3]=t_clip[15]-t_clip[14];
	t_t=1.0/Math.sqrt(this.m_frustum[4][0]*this.m_frustum[4][0]+this.m_frustum[4][1]*this.m_frustum[4][1]+this.m_frustum[4][2]*this.m_frustum[4][2]);
	this.m_frustum[4][0]*=t_t;
	this.m_frustum[4][1]*=t_t;
	this.m_frustum[4][2]*=t_t;
	this.m_frustum[4][3]*=t_t;
	this.m_frustum[5][0]=t_clip[3]+t_clip[2];
	this.m_frustum[5][1]=t_clip[7]+t_clip[6];
	this.m_frustum[5][2]=t_clip[11]+t_clip[10];
	this.m_frustum[5][3]=t_clip[15]+t_clip[14];
	t_t=1.0/Math.sqrt(this.m_frustum[5][0]*this.m_frustum[5][0]+this.m_frustum[5][1]*this.m_frustum[5][1]+this.m_frustum[5][2]*this.m_frustum[5][2]);
	this.m_frustum[5][0]*=t_t;
	this.m_frustum[5][1]*=t_t;
	this.m_frustum[5][2]*=t_t;
	this.m_frustum[5][3]*=t_t;
	return 0;
}
c_TCamera.prototype.p_Update2=function(t_cam){
	this.p_accPerspective(this.m_fov_y,this.m_range_near,this.m_range_far,0.0,0.0);
	this.m_mod_mat=this.m_mat.p_Inverse();
	if(((this.m_eyedx)!=0.0) || ((this.m_eyedy)!=0.0)){
		this.m_mod_mat.p_Translate(-this.m_eyedx,-this.m_eyedy,0.0);
	}
	this.m_view_mat=this.m_mod_mat;
	this.m_projview_mat.p_Overwrite(this.m_proj_mat);
	this.m_projview_mat.p_Multiply4(this.m_mod_mat);
	if((t_cam)!=null){
		this.p_ExtractFrustum();
	}
	return 0;
}
c_TCamera.prototype.p_EntityInFrustum=function(t_ent){
	var t_x=t_ent.p_EntityX(1);
	var t_y=t_ent.p_EntityY(1);
	var t_z=t_ent.p_EntityZ(1);
	var t_radius=bb_math_Abs2(t_ent.m_cull_radius);
	var t_mesh=object_downcast((t_ent),c_TMesh);
	if((t_mesh)!=null){
		var t_r=t_ent.m_mat.p_TransformPoint(t_mesh.m_center_x,t_mesh.m_center_y,t_mesh.m_center_z,1.0);
		t_x=t_r[0];
		t_y=t_r[1];
		t_z=t_r[2];
		var t_gs=bb_math_Max2(bb_math_Max2(t_ent.m_gsx,t_ent.m_gsy),t_ent.m_gsz);
		t_radius=t_radius*t_gs;
		if(t_radius<0.0){
			t_radius=-t_radius;
		}
	}
	var t_d=.0;
	t_d=this.m_frustum[0][0]*t_x+this.m_frustum[0][1]*t_y+this.m_frustum[0][2]*t_z+this.m_frustum[0][3];
	if(t_d<=-t_radius){
		return 0.0;
	}
	t_d=this.m_frustum[1][0]*t_x+this.m_frustum[1][1]*t_y+this.m_frustum[1][2]*t_z+this.m_frustum[1][3];
	if(t_d<=-t_radius){
		return 0.0;
	}
	t_d=this.m_frustum[2][0]*t_x+this.m_frustum[2][1]*t_y+this.m_frustum[2][2]*t_z+this.m_frustum[2][3];
	if(t_d<=-t_radius){
		return 0.0;
	}
	t_d=this.m_frustum[3][0]*t_x+this.m_frustum[3][1]*t_y+this.m_frustum[3][2]*t_z+this.m_frustum[3][3];
	if(t_d<=-t_radius){
		return 0.0;
	}
	t_d=this.m_frustum[4][0]*t_x+this.m_frustum[4][1]*t_y+this.m_frustum[4][2]*t_z+this.m_frustum[4][3];
	if(t_d<=-t_radius){
		return 0.0;
	}
	t_d=this.m_frustum[5][0]*t_x+this.m_frustum[5][1]*t_y+this.m_frustum[5][2]*t_z+this.m_frustum[5][3];
	if(t_d<=-t_radius){
		return 0.0;
	}
	return t_d+t_radius;
}
c_TCamera.prototype.p_CameraClsMode=function(t_color,t_zbuffer){
	this.m_cls_color=t_color;
	this.m_cls_zbuffer=t_zbuffer;
	return 0;
}
c_TCamera.prototype.p_CameraScissor=function(t_x,t_y,t_w,t_h){
	if(t_x<0){
		t_x=0;
	}
	if(t_x>c_TRender.m_width){
		t_x=c_TRender.m_width;
	}
	if(t_y<0){
		t_y=0;
	}
	if(t_y>c_TRender.m_height){
		t_y=c_TRender.m_height;
	}
	if(t_w<0){
		t_w=0;
	}
	if(t_w>c_TRender.m_width){
		t_w=c_TRender.m_width;
	}
	if(t_h<0){
		t_h=0;
	}
	if(t_h>c_TRender.m_height){
		t_h=c_TRender.m_height;
	}
	t_y=c_TRender.m_height-t_h-t_y;
	this.m_viewport[0]=t_x;
	this.m_viewport[1]=t_y;
	this.m_viewport[2]=t_w;
	this.m_viewport[3]=t_h;
	return 0;
}
function bb_monkeyutility_AllocateFloatArray(t_i,t_j){
	var t_arr=new_array_array(t_i);
	for(var t_ind=0;t_ind<t_i;t_ind=t_ind+1){
		t_arr[t_ind]=new_number_array(t_j);
	}
	return t_arr;
}
function c_List4(){
	Object.call(this);
	this.m__head=(c_HeadNode4.m_new.call(new c_HeadNode4));
}
c_List4.m_new=function(){
	return this;
}
c_List4.prototype.p_AddLast4=function(t_data){
	return c_Node5.m_new.call(new c_Node5,this.m__head,this.m__head.m__pred,t_data);
}
c_List4.m_new2=function(t_data){
	var t_=t_data;
	var t_2=0;
	while(t_2<t_.length){
		var t_t=t_[t_2];
		t_2=t_2+1;
		this.p_AddLast4(t_t);
	}
	return this;
}
c_List4.prototype.p_Count=function(){
	var t_n=0;
	var t_node=this.m__head.m__succ;
	while(t_node!=this.m__head){
		t_node=t_node.m__succ;
		t_n+=1;
	}
	return t_n;
}
c_List4.prototype.p_FirstNode=function(){
	if(this.m__head.m__succ!=this.m__head){
		return this.m__head.m__succ;
	}
	return null;
}
c_List4.prototype.p_AddFirst2=function(t_data){
	return c_Node5.m_new.call(new c_Node5,this.m__head.m__succ,this.m__head,t_data);
}
c_List4.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator11.m_new.call(new c_Enumerator11,this);
}
function c_EntityList2(){
	c_List4.call(this);
}
c_EntityList2.prototype=extend_class(c_List4);
c_EntityList2.m_new=function(){
	c_List4.m_new.call(this);
	return this;
}
c_EntityList2.prototype.p_EntityListAdd2=function(t_obj){
	var t_llink=this.p_FirstNode();
	if(t_obj.m_order>0){
		t_llink=this.p_AddFirst2(t_obj);
		return t_llink;
	}else{
		t_llink=this.p_AddLast4(t_obj);
		return t_llink;
	}
}
function c_Node5(){
	Object.call(this);
	this.m__succ=null;
	this.m__pred=null;
	this.m__data=null;
}
c_Node5.m_new=function(t_succ,t_pred,t_data){
	this.m__succ=t_succ;
	this.m__pred=t_pred;
	this.m__succ.m__pred=this;
	this.m__pred.m__succ=this;
	this.m__data=t_data;
	return this;
}
c_Node5.m_new2=function(){
	return this;
}
function c_HeadNode4(){
	c_Node5.call(this);
}
c_HeadNode4.prototype=extend_class(c_Node5);
c_HeadNode4.m_new=function(){
	c_Node5.m_new2.call(this);
	this.m__succ=(this);
	this.m__pred=(this);
	return this;
}
function bb_functions_CreateCamera(t_parent){
	return c_TCamera.m_CreateCamera(t_parent);
}
function c_TBone(){
	c_TEntity.call(this);
	this.m_rest_mat=c_Matrix.m_new.call(new c_Matrix);
	this.m_mat2=c_Matrix.m_new.call(new c_Matrix);
	this.m_tform_mat=c_Matrix.m_new.call(new c_Matrix);
	this.m_inv_mat=c_Matrix.m_new.call(new c_Matrix);
	this.m_kx=.0;
	this.m_ky=.0;
	this.m_kz=.0;
	this.m_keys=null;
	this.m_n_px=.0;
	this.m_n_py=.0;
	this.m_n_pz=.0;
	this.m_kqw=.0;
	this.m_kqx=.0;
	this.m_kqy=.0;
	this.m_kqz=.0;
	this.m_n_qw=.0;
	this.m_n_qx=.0;
	this.m_n_qy=.0;
	this.m_n_qz=.0;
}
c_TBone.prototype=extend_class(c_TEntity);
c_TBone.m_t_mat=null;
c_TBone.m_new_mat=null;
c_TBone.prototype.p_UpdateMatrix=function(t_mat0){
	this.m_loc_mat.p_Overwrite(t_mat0);
	this.m_mat2.p_Overwrite(this.m_loc_mat);
	if(this.m_parent!=null){
		this.m_mat.p_Overwrite(this.m_parent.m_mat);
		this.m_mat.p_Multiply(this.m_loc_mat);
		this.m_gsx=this.m_parent.m_gsx*this.m_sx;
		this.m_gsy=this.m_parent.m_gsy*this.m_sy;
		this.m_gsz=this.m_parent.m_gsz*this.m_sz;
	}
	if(object_downcast((this.m_parent),c_TBone)!=null){
		c_TBone.m_new_mat.p_Overwrite(object_downcast((this.m_parent),c_TBone).m_mat2);
		c_TBone.m_new_mat.p_Multiply(this.m_loc_mat);
		this.m_mat2.p_Overwrite(c_TBone.m_new_mat);
	}
	this.m_tform_mat.p_Overwrite(this.m_mat2);
	this.m_tform_mat.p_Multiply(this.m_inv_mat);
}
c_TBone.m_UpdateNonBoneChild=function(t_ent_p){
	if(object_downcast((t_ent_p),c_TBone)==null){
		c_TBone.m_new_mat.p_Overwrite(t_ent_p.m_parent.m_mat);
		if(object_downcast((t_ent_p),c_TBone)!=null){
			c_TBone.m_new_mat.m_grid[3][2]=-c_TBone.m_new_mat.m_grid[3][2];
		}
		t_ent_p.m_mat.p_Overwrite(c_TBone.m_new_mat);
		t_ent_p.p_UpdateMat(false);
	}
}
c_TBone.m_UpdateBoneChildren=function(t_p){
	var t_=t_p.m_child_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_ent=t_.p_NextObject();
		var t_bo=object_downcast((t_ent),c_TBone);
		if(t_bo!=null){
			t_bo.p_UpdateMatrix(t_bo.m_loc_mat);
		}else{
			c_TBone.m_UpdateNonBoneChild(t_ent);
		}
		c_TBone.m_UpdateBoneChildren(t_ent);
	}
}
c_TBone.prototype.p_PositionBone=function(t_x,t_y,t_z,t_glob){
	this.m_px=t_x;
	this.m_py=t_y;
	this.m_pz=t_z;
	c_TBone.m_t_mat.p_Overwrite(this.m_rest_mat);
	c_TBone.m_t_mat.p_Multiply(this.m_loc_mat);
	c_TBone.m_t_mat.m_grid[3][0]=t_x+this.m_rest_mat.m_grid[3][0];
	c_TBone.m_t_mat.m_grid[3][1]=t_y+this.m_rest_mat.m_grid[3][1];
	c_TBone.m_t_mat.m_grid[3][2]=t_z+this.m_rest_mat.m_grid[3][2];
	this.p_UpdateMatrix(c_TBone.m_t_mat);
	if(this.m_child_list.p_IsEmpty()!=true){
		c_TBone.m_UpdateBoneChildren(this);
	}
	return 0;
}
c_TBone.prototype.p_RotateBone=function(t_x,t_y,t_z,t_glob){
	this.m_rx=-t_x;
	this.m_ry=t_y;
	this.m_rz=t_z;
	c_TBone.m_t_mat.p_Overwrite(this.m_rest_mat);
	c_TBone.m_t_mat.m_grid[3][0]=this.m_px+this.m_rest_mat.m_grid[3][0];
	c_TBone.m_t_mat.m_grid[3][1]=this.m_py+this.m_rest_mat.m_grid[3][1];
	c_TBone.m_t_mat.m_grid[3][2]=this.m_pz+this.m_rest_mat.m_grid[3][2];
	c_TBone.m_t_mat.p_Rotate(t_x,t_y,t_z);
	this.p_UpdateMatrix(c_TBone.m_t_mat);
	if(this.m_child_list.p_IsEmpty()!=true){
		c_TBone.m_UpdateBoneChildren(this);
	}
	return 0;
}
c_TBone.prototype.p_ScaleBone=function(t_x,t_y,t_z,t_glob){
	this.m_sx=t_x;
	this.m_sy=t_y;
	this.m_sz=t_z;
	c_TBone.m_t_mat.p_Overwrite(this.m_rest_mat);
	c_TBone.m_t_mat.m_grid[3][0]=this.m_px+this.m_rest_mat.m_grid[3][0];
	c_TBone.m_t_mat.m_grid[3][1]=this.m_py+this.m_rest_mat.m_grid[3][1];
	c_TBone.m_t_mat.m_grid[3][2]=this.m_pz+this.m_rest_mat.m_grid[3][2];
	c_TBone.m_t_mat.p_Rotate(this.m_rx,this.m_ry,this.m_rz);
	c_TBone.m_t_mat.p_Scale(t_x,t_y,t_z);
	this.p_UpdateMatrix(c_TBone.m_t_mat);
	this.m_gsx=this.m_parent.m_gsx*this.m_sx;
	this.m_gsy=this.m_parent.m_gsy*this.m_sy;
	this.m_gsz=this.m_parent.m_gsz*this.m_sz;
	if(this.m_child_list.p_IsEmpty()!=true){
		c_TBone.m_UpdateBoneChildren(this);
	}
	return 0;
}
c_TBone.prototype.p_Transform=function(t_pos,t_quat,t_update_children){
	c_Quaternion.m_QuatToMatrix(t_quat.m_x,t_quat.m_y,t_quat.m_z,t_quat.m_w,c_TBone.m_new_mat);
	c_TBone.m_new_mat.m_grid[3][0]=t_pos.m_x;
	c_TBone.m_new_mat.m_grid[3][1]=t_pos.m_y;
	c_TBone.m_new_mat.m_grid[3][2]=t_pos.m_z;
	this.m_px=t_pos.m_x;
	this.m_py=t_pos.m_y;
	this.m_pz=t_pos.m_z;
	var t_mx=c_TBone.m_new_mat.m_grid[2][0];
	var t_my=c_TBone.m_new_mat.m_grid[2][1];
	var t_mz=c_TBone.m_new_mat.m_grid[2][2];
	this.m_rx=-(Math.atan2(t_mx,t_mz)*R2D);
	this.m_ry=-(Math.atan2(t_my,Math.sqrt(t_mx*t_mx+t_mz*t_mz))*R2D);
	this.m_rz=(Math.atan2(c_TBone.m_new_mat.m_grid[0][1],c_TBone.m_new_mat.m_grid[1][1])*R2D);
	this.p_UpdateMatrix(c_TBone.m_new_mat);
	if(t_update_children){
		if(this.m_child_list.p_IsEmpty()!=true){
			c_TEntity.m_UpdateChildren((this),0);
		}
	}
}
function c_Line(){
	Object.call(this);
	this.m_o=null;
	this.m_d=null;
}
c_Line.m_new=function(){
	this.m_o=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_d=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	return this;
}
c_Line.m_new2=function(t_oo,t_dd){
	this.m_o=t_oo.p_Copy();
	this.m_d=t_dd.p_Copy();
	return this;
}
c_Line.m_new3=function(t_ox,t_oy,t_oz,t_dx,t_dy,t_dz){
	this.m_o=c_Vector.m_new.call(new c_Vector,t_ox,t_oy,t_oz);
	this.m_d=c_Vector.m_new.call(new c_Vector,t_dx,t_dy,t_dz);
	return this;
}
c_Line.prototype.p_Update3=function(t_ox,t_oy,t_oz,t_dx,t_dy,t_dz){
	this.m_o.m_x=t_ox;
	this.m_o.m_y=t_oy;
	this.m_o.m_z=t_oz;
	this.m_d.m_x=t_dx;
	this.m_d.m_y=t_dy;
	this.m_d.m_z=t_dz;
	return 0;
}
c_Line.prototype.p_Update4=function(t_l){
	this.m_o.m_x=t_l.m_o.m_x;
	this.m_o.m_y=t_l.m_o.m_y;
	this.m_o.m_z=t_l.m_o.m_z;
	this.m_d.m_x=t_l.m_d.m_x;
	this.m_d.m_y=t_l.m_d.m_y;
	this.m_d.m_z=t_l.m_d.m_z;
	return 0;
}
c_Line.prototype.p_Multiply5=function(t_q){
	return c_Vector.m_new.call(new c_Vector,this.m_o.m_x+this.m_d.m_x*t_q,this.m_o.m_y+this.m_d.m_y*t_q,this.m_o.m_z+this.m_d.m_z*t_q);
}
c_Line.prototype.p_Length=function(){
	var t_x=this.m_o.m_x-this.m_d.m_x;
	var t_y=this.m_o.m_y-this.m_d.m_y;
	var t_z=this.m_o.m_z-this.m_d.m_z;
	return Math.sqrt(t_x*t_x+t_y*t_y+t_z*t_z);
}
function c_Enumerator2(){
	Object.call(this);
	this.m__list=null;
	this.m__curr=null;
}
c_Enumerator2.m_new=function(t_list){
	this.m__list=t_list;
	this.m__curr=t_list.m__head.m__succ;
	return this;
}
c_Enumerator2.m_new2=function(){
	return this;
}
c_Enumerator2.prototype.p_HasNext=function(){
	while(this.m__curr.m__succ.m__pred!=this.m__curr){
		this.m__curr=this.m__curr.m__succ;
	}
	return this.m__curr!=this.m__list.m__head;
}
c_Enumerator2.prototype.p_NextObject=function(){
	var t_data=this.m__curr.m__data;
	this.m__curr=this.m__curr.m__succ;
	return t_data;
}
function c_TLight(){
	c_TEntity.call(this);
	this.m_light_type=0;
	this.m_light_link=null;
	this.m_const_att=0.5;
	this.m_lin_att=1.0;
	this.m_quad_att=0.0;
	this.m_actual_range=1000.0;
	this.m_red=1.0;
	this.m_green=1.0;
	this.m_blue=1.0;
	this.m_outer_ang=45.0;
	this.m_inner_ang=0.0;
	this.m_spot_exp=10.0;
}
c_TLight.prototype=extend_class(c_TEntity);
c_TLight.m_no_lights=0;
c_TLight.m_max_lights=0;
c_TLight.m_new=function(){
	c_TEntity.m_new.call(this);
	return this;
}
c_TLight.m_light_list=null;
c_TLight.m_CreateLight=function(t_l_type,t_parent_ent){
	if(c_TLight.m_no_lights>=c_TLight.m_max_lights){
		return null;
	}
	var t_light=c_TLight.m_new.call(new c_TLight);
	t_light.m_light_type=t_l_type;
	t_light.m_classname="Light";
	c_TLight.m_no_lights=c_TLight.m_no_lights+1;
	t_light.m_light_link=c_TLight.m_light_list.p_AddLast5(t_light);
	t_light.m_entity_link=c_TEntity.m_entity_list.p_EntityListAdd(t_light);
	if((t_parent_ent)!=null){
		t_light.p_AddParent(t_parent_ent);
	}
	if(t_light.m_light_type==1){
		t_light.m_const_att=10.0;
		t_light.m_lin_att=10.0;
	}
	if(t_light.m_parent!=null){
		t_light.m_mat.p_Overwrite(t_light.m_parent.m_mat);
		t_light.p_UpdateMat(false);
	}else{
		t_light.p_UpdateMat(true);
	}
	return t_light;
}
c_TLight.m_ambient_red=0;
c_TLight.m_ambient_green=0;
c_TLight.m_ambient_blue=0;
function c_List5(){
	Object.call(this);
	this.m__head=(c_HeadNode5.m_new.call(new c_HeadNode5));
}
c_List5.m_new=function(){
	return this;
}
c_List5.prototype.p_AddLast5=function(t_data){
	return c_Node6.m_new.call(new c_Node6,this.m__head,this.m__head.m__pred,t_data);
}
c_List5.m_new2=function(t_data){
	var t_=t_data;
	var t_2=0;
	while(t_2<t_.length){
		var t_t=t_[t_2];
		t_2=t_2+1;
		this.p_AddLast5(t_t);
	}
	return this;
}
c_List5.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator13.m_new.call(new c_Enumerator13,this);
}
function c_Node6(){
	Object.call(this);
	this.m__succ=null;
	this.m__pred=null;
	this.m__data=null;
}
c_Node6.m_new=function(t_succ,t_pred,t_data){
	this.m__succ=t_succ;
	this.m__pred=t_pred;
	this.m__succ.m__pred=this;
	this.m__pred.m__succ=this;
	this.m__data=t_data;
	return this;
}
c_Node6.m_new2=function(){
	return this;
}
function c_HeadNode5(){
	c_Node6.call(this);
}
c_HeadNode5.prototype=extend_class(c_Node6);
c_HeadNode5.m_new=function(){
	c_Node6.m_new2.call(this);
	this.m__succ=(this);
	this.m__pred=(this);
	return this;
}
function bb_functions_CreateLight(t_light_type,t_parent){
	return c_TLight.m_CreateLight(t_light_type,t_parent);
}
function c_Cal3dB3d(){
	Object.call(this);
	this.m_B3dModel=null;
	this.m_Meshes=null;
	this.m_Anims=null;
	this.m_CoreModel=null;
	this.m_Skele=null;
	this.m_CModel=null;
	this.m_cal_renderer=null;
}
c_Cal3dB3d.m_new=function(){
	this.m_B3dModel=c_TMesh.m_CreateMesh(null);
	this.m_Meshes=c_List6.m_new.call(new c_List6);
	this.m_Anims=c_List7.m_new.call(new c_List7);
	this.m_CoreModel=(new Cal3D.CalCoreModel);
	return this;
}
c_Cal3dB3d.prototype.p_SetSkeleton=function(t_url){
	this.m_Skele=Cal3D.CalLoader.parseXmlCoreSkeleton(Cal3D.CalLoader.StringtoXml(bb_app_LoadString(t_url)));
	this.m_CoreModel.setCoreSkeleton(this.m_Skele);
	return 0;
}
c_Cal3dB3d.prototype.p_AddMesh=function(t_url){
	var t_msh=null;
	t_msh=Cal3D.CalLoader.parseXmlCoreMesh(Cal3D.CalLoader.StringtoXml(bb_app_LoadString(t_url)));
	if(t_msh!=null){
		var t_c3db3dm=c_Cal3dB3dMesh.m_new.call(new c_Cal3dB3dMesh);
		t_c3db3dm.m_MID=this.m_CoreModel.addCoreMesh(t_msh);
		print("Added: "+String(t_c3db3dm.m_MID));
		t_c3db3dm.m_CMesh=t_msh;
		t_c3db3dm.m_B3dMesh=c_TMesh.m_CreateMesh(this.m_B3dModel);
		t_c3db3dm.m_B3dMesh.m_anim=2;
		this.m_Meshes.p_AddLast6(t_c3db3dm);
	}
	return 0;
}
c_Cal3dB3d.prototype.p_AddAnim=function(t_url){
	if(this.m_Skele==null){
		return 0;
	}
	var t_anm=null;
	t_anm=Cal3D.CalLoader.parseXmlCoreAnimation(Cal3D.CalLoader.StringtoXml(bb_app_LoadString(t_url)),this.m_Skele);
	if(t_anm!=null){
		var t_aa=this.m_CoreModel.addCoreAnimation(t_anm);
		print("Loaded Anim: "+String(t_aa));
		this.m_Anims.p_AddLast7(t_anm);
	}
	return 0;
}
c_Cal3dB3d.prototype.p_Build=function(){
	this.m_CModel=this.m_CoreModel.makemodel();
	var t_=this.m_Meshes.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_cb3d=t_.p_NextObject();
		this.m_CModel.attachMesh(t_cb3d.m_MID);
	}
	if(this.m_CModel!=null){
		print("Have Cmodel");
	}
	this.m_cal_renderer=this.m_CModel.getRenderer();
	this.m_cal_renderer.beginRendering();
	var t_mc=this.m_cal_renderer.getMeshCount();
	print("Mesh Count: "+String(t_mc));
	for(var t_i=0;t_i<=t_mc;t_i=t_i+1){
		var t_2=this.m_Meshes.p_ObjectEnumerator();
		while(t_2.p_HasNext()){
			var t_cb3d2=t_2.p_NextObject();
			if(t_cb3d2.m_MID==t_i){
				print("Got Mesh: "+String(t_i));
				var t_submeshCount=this.m_cal_renderer.getSubmeshCount(t_i);
				print("NOF: Submeshes "+String(t_submeshCount));
				for(var t_ii=0;t_ii<=t_submeshCount-1;t_ii=t_ii+1){
					this.m_cal_renderer.selectMeshSubmesh(t_cb3d2.m_MID,t_ii);
					var t_vertexCount=this.m_cal_renderer.getVertexCount();
					var t_faceCount=this.m_cal_renderer.getFaceCount();
					print("Verts: "+String(t_vertexCount)+" Faces"+String(t_faceCount));
					var t_faces=new_number_array(t_faceCount*3);
					var t_vertices=new_number_array(t_vertexCount*3);
					this.m_cal_renderer.getVertices(t_vertices,3);
					this.m_cal_renderer.getFaces(t_faces);
					t_cb3d2.p_Build3(t_vertices,t_faces);
				}
			}
		}
	}
	this.m_cal_renderer.endRendering();
	return 0;
}
c_Cal3dB3d.prototype.p_Update5=function(t_d){
	this.m_CModel.update(t_d);
	this.m_cal_renderer.beginRendering();
	var t_mc=this.m_cal_renderer.getMeshCount();
	for(var t_i=0;t_i<=t_mc;t_i=t_i+1){
		var t_=this.m_Meshes.p_ObjectEnumerator();
		while(t_.p_HasNext()){
			var t_cb3d=t_.p_NextObject();
			if(t_cb3d.m_MID==t_i){
				var t_submeshCount=this.m_cal_renderer.getSubmeshCount(t_i);
				for(var t_ii=0;t_ii<=t_submeshCount-1;t_ii=t_ii+1){
					this.m_cal_renderer.selectMeshSubmesh(t_cb3d.m_MID,t_ii);
					var t_vertexCount=this.m_cal_renderer.getVertexCount();
					var t_faceCount=this.m_cal_renderer.getFaceCount();
					var t_vertices=new_number_array(t_vertexCount*3);
					var t_normals=new_number_array(t_vertexCount*3);
					var t_faces=new_number_array(t_faceCount*3);
					this.m_cal_renderer.getVertices(t_vertices,3);
					this.m_cal_renderer.getNormals(t_normals,3);
					this.m_cal_renderer.getFaces(t_faces);
					t_cb3d.p_Update6(t_ii,t_vertices,t_normals,t_faces);
				}
			}
		}
	}
	this.m_cal_renderer.endRendering();
	return 0;
}
function c_Cal3dB3dMesh(){
	Object.call(this);
	this.m_MID=0;
	this.m_CMesh=null;
	this.m_B3dMesh=null;
	this.m_surf=new_object_array(0);
}
c_Cal3dB3dMesh.m_new=function(){
	return this;
}
c_Cal3dB3dMesh.prototype.p_Build2=function(t_v,t_f){
	if(this.m_CMesh==null){
		return 0;
	}
	if(this.m_B3dMesh==null){
		return 0;
	}
	var t_ind=this.m_surf.length;
	this.m_surf=resize_object_array(this.m_surf,this.m_surf.length+1);
	print("Building Surf: "+String(t_ind));
	this.m_surf[t_ind]=this.m_B3dMesh.p_CreateSurface(null);
	for(var t_i=0;t_i<=t_v.length-1;t_i=t_i+1){
	}
	for(var t_t=0;t_t<=t_f.length-1;t_t=t_t+1){
	}
	this.m_surf[t_ind].p_UpdateNormals(false);
	return 0;
}
c_Cal3dB3dMesh.prototype.p_Build3=function(t_v,t_f){
	if(this.m_CMesh==null){
		return 0;
	}
	if(this.m_B3dMesh==null){
		return 0;
	}
	var t_ind=this.m_surf.length;
	this.m_surf=resize_object_array(this.m_surf,this.m_surf.length+1);
	print("Building Surf: "+String(t_ind));
	this.m_surf[t_ind]=this.m_B3dMesh.p_CreateSurface(null);
	for(var t_i=0;t_i<=t_v.length-1;t_i=t_i+3){
		this.m_surf[t_ind].p_AddVertex(t_v[t_i],t_v[t_i+1],-t_v[t_i+2],0.0,0.0,0.0);
	}
	this.m_surf[t_ind].p_AddTriangle2(t_f,-1);
	this.m_surf[t_ind].p_UpdateNormals(false);
	return 0;
}
c_Cal3dB3dMesh.prototype.p_Update6=function(t_si,t_v,t_n,t_f){
	this.m_surf[t_si].p_ClearSurface(true,true,false);
	for(var t_i=0;t_i<=t_v.length-1;t_i=t_i+3){
		var t_vid=this.m_surf[t_si].p_AddVertex(t_v[t_i],t_v[t_i+1],-t_v[t_i+2],0.0,0.0,0.0);
		this.m_surf[t_si].p_VertexNormal(t_vid,t_n[t_i],t_n[t_i+1],-t_n[t_i+2]);
	}
	this.m_surf[t_si].p_AddTriangle2(t_f,-1);
	return 0;
}
c_Cal3dB3dMesh.prototype.p_Update7=function(t_si,t_v,t_n,t_f){
	this.m_surf[t_si].p_ClearSurface(true,true,false);
	for(var t_i=0;t_i<=t_v.length-1;t_i=t_i+1){
	}
	for(var t_t=0;t_t<=t_f.length-1;t_t=t_t+1){
	}
	return 0;
}
function c_List6(){
	Object.call(this);
	this.m__head=(c_HeadNode6.m_new.call(new c_HeadNode6));
}
c_List6.m_new=function(){
	return this;
}
c_List6.prototype.p_AddLast6=function(t_data){
	return c_Node7.m_new.call(new c_Node7,this.m__head,this.m__head.m__pred,t_data);
}
c_List6.m_new2=function(t_data){
	var t_=t_data;
	var t_2=0;
	while(t_2<t_.length){
		var t_t=t_[t_2];
		t_2=t_2+1;
		this.p_AddLast6(t_t);
	}
	return this;
}
c_List6.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator3.m_new.call(new c_Enumerator3,this);
}
function c_Node7(){
	Object.call(this);
	this.m__succ=null;
	this.m__pred=null;
	this.m__data=null;
}
c_Node7.m_new=function(t_succ,t_pred,t_data){
	this.m__succ=t_succ;
	this.m__pred=t_pred;
	this.m__succ.m__pred=this;
	this.m__pred.m__succ=this;
	this.m__data=t_data;
	return this;
}
c_Node7.m_new2=function(){
	return this;
}
function c_HeadNode6(){
	c_Node7.call(this);
}
c_HeadNode6.prototype=extend_class(c_Node7);
c_HeadNode6.m_new=function(){
	c_Node7.m_new2.call(this);
	this.m__succ=(this);
	this.m__pred=(this);
	return this;
}
function c_List7(){
	Object.call(this);
	this.m__head=(c_HeadNode7.m_new.call(new c_HeadNode7));
}
c_List7.m_new=function(){
	return this;
}
c_List7.prototype.p_AddLast7=function(t_data){
	return c_Node8.m_new.call(new c_Node8,this.m__head,this.m__head.m__pred,t_data);
}
c_List7.m_new2=function(t_data){
	var t_=t_data;
	var t_2=0;
	while(t_2<t_.length){
		var t_t=t_[t_2];
		t_2=t_2+1;
		this.p_AddLast7(t_t);
	}
	return this;
}
function c_Node8(){
	Object.call(this);
	this.m__succ=null;
	this.m__pred=null;
	this.m__data=null;
}
c_Node8.m_new=function(t_succ,t_pred,t_data){
	this.m__succ=t_succ;
	this.m__pred=t_pred;
	this.m__succ.m__pred=this;
	this.m__pred.m__succ=this;
	this.m__data=t_data;
	return this;
}
c_Node8.m_new2=function(){
	return this;
}
function c_HeadNode7(){
	c_Node8.call(this);
}
c_HeadNode7.prototype=extend_class(c_Node8);
c_HeadNode7.m_new=function(){
	c_Node8.m_new2.call(this);
	this.m__succ=(this);
	this.m__pred=(this);
	return this;
}
function bb_app_LoadString(t_path){
	return bb_app__game.LoadString(bb_data_FixDataPath(t_path));
}
function c_Enumerator3(){
	Object.call(this);
	this.m__list=null;
	this.m__curr=null;
}
c_Enumerator3.m_new=function(t_list){
	this.m__list=t_list;
	this.m__curr=t_list.m__head.m__succ;
	return this;
}
c_Enumerator3.m_new2=function(){
	return this;
}
c_Enumerator3.prototype.p_HasNext=function(){
	while(this.m__curr.m__succ.m__pred!=this.m__curr){
		this.m__curr=this.m__curr.m__succ;
	}
	return this.m__curr!=this.m__list.m__head;
}
c_Enumerator3.prototype.p_NextObject=function(){
	var t_data=this.m__curr.m__data;
	this.m__curr=this.m__curr.m__succ;
	return t_data;
}
function c_NormHelperClass(){
	Object.call(this);
	this.m_vec=null;
	this.m_vert=0;
}
c_NormHelperClass.m_new=function(){
	return this;
}
function c_Map2(){
	Object.call(this);
	this.m_root=null;
}
c_Map2.m_new=function(){
	return this;
}
c_Map2.prototype.p_Compare3=function(t_lhs,t_rhs){
}
c_Map2.prototype.p_FindNode2=function(t_key){
	var t_node=this.m_root;
	while((t_node)!=null){
		var t_cmp=this.p_Compare3(t_key,t_node.m_key);
		if(t_cmp>0){
			t_node=t_node.m_right;
		}else{
			if(t_cmp<0){
				t_node=t_node.m_left;
			}else{
				return t_node;
			}
		}
	}
	return t_node;
}
c_Map2.prototype.p_Get=function(t_key){
	var t_node=this.p_FindNode2(t_key);
	if((t_node)!=null){
		return t_node.m_value;
	}
	return null;
}
c_Map2.prototype.p_RotateLeft2=function(t_node){
	var t_child=t_node.m_right;
	t_node.m_right=t_child.m_left;
	if((t_child.m_left)!=null){
		t_child.m_left.m_parent=t_node;
	}
	t_child.m_parent=t_node.m_parent;
	if((t_node.m_parent)!=null){
		if(t_node==t_node.m_parent.m_left){
			t_node.m_parent.m_left=t_child;
		}else{
			t_node.m_parent.m_right=t_child;
		}
	}else{
		this.m_root=t_child;
	}
	t_child.m_left=t_node;
	t_node.m_parent=t_child;
	return 0;
}
c_Map2.prototype.p_RotateRight2=function(t_node){
	var t_child=t_node.m_left;
	t_node.m_left=t_child.m_right;
	if((t_child.m_right)!=null){
		t_child.m_right.m_parent=t_node;
	}
	t_child.m_parent=t_node.m_parent;
	if((t_node.m_parent)!=null){
		if(t_node==t_node.m_parent.m_right){
			t_node.m_parent.m_right=t_child;
		}else{
			t_node.m_parent.m_left=t_child;
		}
	}else{
		this.m_root=t_child;
	}
	t_child.m_right=t_node;
	t_node.m_parent=t_child;
	return 0;
}
c_Map2.prototype.p_InsertFixup2=function(t_node){
	while(((t_node.m_parent)!=null) && t_node.m_parent.m_color==-1 && ((t_node.m_parent.m_parent)!=null)){
		if(t_node.m_parent==t_node.m_parent.m_parent.m_left){
			var t_uncle=t_node.m_parent.m_parent.m_right;
			if(((t_uncle)!=null) && t_uncle.m_color==-1){
				t_node.m_parent.m_color=1;
				t_uncle.m_color=1;
				t_uncle.m_parent.m_color=-1;
				t_node=t_uncle.m_parent;
			}else{
				if(t_node==t_node.m_parent.m_right){
					t_node=t_node.m_parent;
					this.p_RotateLeft2(t_node);
				}
				t_node.m_parent.m_color=1;
				t_node.m_parent.m_parent.m_color=-1;
				this.p_RotateRight2(t_node.m_parent.m_parent);
			}
		}else{
			var t_uncle2=t_node.m_parent.m_parent.m_left;
			if(((t_uncle2)!=null) && t_uncle2.m_color==-1){
				t_node.m_parent.m_color=1;
				t_uncle2.m_color=1;
				t_uncle2.m_parent.m_color=-1;
				t_node=t_uncle2.m_parent;
			}else{
				if(t_node==t_node.m_parent.m_left){
					t_node=t_node.m_parent;
					this.p_RotateRight2(t_node);
				}
				t_node.m_parent.m_color=1;
				t_node.m_parent.m_parent.m_color=-1;
				this.p_RotateLeft2(t_node.m_parent.m_parent);
			}
		}
	}
	this.m_root.m_color=1;
	return 0;
}
c_Map2.prototype.p_Set2=function(t_key,t_value){
	var t_node=this.m_root;
	var t_parent=null;
	var t_cmp=0;
	while((t_node)!=null){
		t_parent=t_node;
		t_cmp=this.p_Compare3(t_key,t_node.m_key);
		if(t_cmp>0){
			t_node=t_node.m_right;
		}else{
			if(t_cmp<0){
				t_node=t_node.m_left;
			}else{
				t_node.m_value=t_value;
				return false;
			}
		}
	}
	t_node=c_Node9.m_new.call(new c_Node9,t_key,t_value,-1,t_parent);
	if((t_parent)!=null){
		if(t_cmp>0){
			t_parent.m_right=t_node;
		}else{
			t_parent.m_left=t_node;
		}
		this.p_InsertFixup2(t_node);
	}else{
		this.m_root=t_node;
	}
	return true;
}
function c_NormMap(){
	c_Map2.call(this);
}
c_NormMap.prototype=extend_class(c_Map2);
c_NormMap.m_new=function(){
	c_Map2.m_new.call(this);
	return this;
}
c_NormMap.prototype.p_Compare3=function(t_lhs,t_rhs){
	if(t_lhs.m_x<t_rhs.m_x){
		return -1;
	}
	if(t_lhs.m_x>t_rhs.m_x){
		return 1;
	}
	if(t_lhs.m_y<t_rhs.m_y){
		return -1;
	}
	if(t_lhs.m_y>t_rhs.m_y){
		return 1;
	}
	if(t_lhs.m_z<t_rhs.m_z){
		return -1;
	}
	return ((t_lhs.m_z>t_rhs.m_z)?1:0);
}
function c_ShortBuffer(){
	Object.call(this);
	this.m_buf=null;
}
c_ShortBuffer.m_new=function(){
	return this;
}
c_ShortBuffer.prototype.p_Peek=function(t_i){
	return this.m_buf.PeekShort(t_i*2);
}
c_ShortBuffer.m_i2f=null;
c_ShortBuffer.m_Create=function(t_i){
	c_ShortBuffer.m_i2f=bb_monkeybuffer_CreateDataBuffer(4);
	var t_b=c_ShortBuffer.m_new.call(new c_ShortBuffer);
	t_b.m_buf=bb_monkeybuffer_CreateDataBuffer(t_i*2+1);
	return t_b;
}
c_ShortBuffer.prototype.p_Length=function(){
	return (((bb_monkeybuffer_GetBufferLength(this.m_buf))*0.5)|0);
}
c_ShortBuffer.prototype.p_Poke=function(t_i,t_arr,t_len){
	if(t_len==-1){
		t_len=t_arr.length;
	}
	for(var t_v=0;t_v<=t_len-1;t_v=t_v+1){
		this.m_buf.PokeShort((t_i+t_v)*2,t_arr[t_v]);
	}
}
c_ShortBuffer.prototype.p_Poke2=function(t_i,t_v){
	this.m_buf.PokeShort(t_i*2,t_v);
}
function c_DataBuffer(){
	BBDataBuffer.call(this);
}
c_DataBuffer.prototype=extend_class(BBDataBuffer);
c_DataBuffer.m_new=function(t_length){
	if(!this._New(t_length)){
		error("Allocate DataBuffer failed");
	}
	return this;
}
c_DataBuffer.m_new2=function(){
	return this;
}
function c_VertexDataBuffer(){
	Object.call(this);
	this.m_buf=null;
}
c_VertexDataBuffer.m_new=function(){
	return this;
}
c_VertexDataBuffer.prototype.p_VertexX=function(t_vid){
	return this.m_buf.PeekFloat(t_vid*64+0+0);
}
c_VertexDataBuffer.prototype.p_VertexY=function(t_vid){
	return this.m_buf.PeekFloat(t_vid*64+0+4);
}
c_VertexDataBuffer.prototype.p_VertexZ=function(t_vid){
	return this.m_buf.PeekFloat(t_vid*64+0+8);
}
c_VertexDataBuffer.prototype.p_PeekVertCoords=function(t_vid){
	var t_v=t_vid*64+0;
	return c_Vector.m_new.call(new c_Vector,this.m_buf.PeekFloat(t_v+0),this.m_buf.PeekFloat(t_v+4),this.m_buf.PeekFloat(t_v+8));
}
c_VertexDataBuffer.prototype.p_PokeNormals=function(t_i,t_x,t_y,t_z){
	var t_index=t_i*64+16;
	this.m_buf.PokeFloat(t_index,t_x);
	this.m_buf.PokeFloat(t_index+4,t_y);
	this.m_buf.PokeFloat(t_index+8,t_z);
	return 0;
}
c_VertexDataBuffer.m_Create=function(t_i){
	var t_b=c_VertexDataBuffer.m_new.call(new c_VertexDataBuffer);
	t_b.m_buf=bb_monkeybuffer_CreateDataBuffer((t_i+1)*64);
	return t_b;
}
c_VertexDataBuffer.prototype.p_PokeFloatArray=function(t_i,t_arr,t_len){
	if(t_len==-1){
		t_len=t_arr.length;
	}
	for(var t_v=0;t_v<=t_len-1;t_v=t_v+1){
		this.m_buf.PokeFloat(t_i*64+t_v*4,t_arr[t_v]);
	}
}
c_VertexDataBuffer.prototype.p_PokeTexCoords0=function(t_i,t_s0,t_t0){
	var t_index=t_i*64+48;
	this.m_buf.PokeFloat(t_index,t_s0);
	this.m_buf.PokeFloat(t_index+4,t_t0);
	return 0;
}
c_VertexDataBuffer.prototype.p_PokeTexCoords1=function(t_i,t_s1,t_t1){
	var t_index=t_i*64+48;
	this.m_buf.PokeFloat(t_index+8,t_s1);
	this.m_buf.PokeFloat(t_index+12,t_t1);
	return 0;
}
c_VertexDataBuffer.prototype.p_GetVertCoords=function(t_vec,t_vid){
	var t_v=t_vid*64+0;
	t_vec.m_x=this.m_buf.PeekFloat(t_v+0);
	t_vec.m_y=this.m_buf.PeekFloat(t_v+4);
	t_vec.m_z=this.m_buf.PeekFloat(t_v+8);
}
c_VertexDataBuffer.prototype.p_PokeVertCoords=function(t_i,t_x,t_y,t_z){
	var t_index=t_i*64+0;
	this.m_buf.PokeFloat(t_index,t_x);
	this.m_buf.PokeFloat(t_index+4,t_y);
	this.m_buf.PokeFloat(t_index+8,t_z);
	return 0;
}
c_VertexDataBuffer.prototype.p_PokeColor=function(t_i,t_r,t_g,t_b,t_a){
	var t_index=t_i*64+32;
	this.m_buf.PokeFloat(t_index,t_r);
	this.m_buf.PokeFloat(t_index+4,t_g);
	this.m_buf.PokeFloat(t_index+8,t_b);
	this.m_buf.PokeFloat(t_index+12,t_a);
	return 0;
}
function c_Node9(){
	Object.call(this);
	this.m_key=null;
	this.m_right=null;
	this.m_left=null;
	this.m_value=null;
	this.m_color=0;
	this.m_parent=null;
}
c_Node9.m_new=function(t_key,t_value,t_color,t_parent){
	this.m_key=t_key;
	this.m_value=t_value;
	this.m_color=t_color;
	this.m_parent=t_parent;
	return this;
}
c_Node9.m_new2=function(){
	return this;
}
function bb_monkeybuffer_CreateDataBuffer(t_i){
	return c_DataBuffer.m_new.call(new c_DataBuffer,t_i);
}
function bb_monkeybuffer_GetBufferLength(t_buf){
	return t_buf.Length();
}
function bb_monkeybuffer_CopyDataBuffer(t_src,t_dest){
	if(t_src==null){
		return t_dest;
	}
	var t_size=bb_monkeybuffer_GetBufferLength(t_src);
	if(bb_monkeybuffer_GetBufferLength(t_dest)<t_size){
		t_size=bb_monkeybuffer_GetBufferLength(t_dest);
	}
	for(var t_i=0;t_i<=t_size-1;t_i=t_i+1){
		t_dest.PokeByte(t_i,t_src.PeekByte(t_i));
	}
	return t_dest;
}
function bb_monkeybuffer_CopyDataBuffer2(t_src,t_dest){
	if(t_src.m_buf==null){
		return t_dest;
	}
	var t_size=bb_monkeybuffer_GetBufferLength(t_src.m_buf);
	if(bb_monkeybuffer_GetBufferLength(t_dest.m_buf)<t_size){
		t_size=bb_monkeybuffer_GetBufferLength(t_dest.m_buf);
	}
	for(var t_i=0;t_i<=t_size-1;t_i=t_i+4){
		t_dest.m_buf.PokeInt(t_i,t_src.m_buf.PeekInt(t_i));
	}
	return t_dest;
}
function bb_monkeybuffer_CopyDataBuffer3(t_src,t_dest,t_begin,t_bend){
	if(t_src.m_buf==null){
		return t_dest;
	}
	t_begin*=64;
	t_bend*=64;
	if(t_begin==0 && t_bend==0){
		t_bend=bb_monkeybuffer_GetBufferLength(t_src.m_buf)-1;
	}
	if(bb_monkeybuffer_GetBufferLength(t_dest.m_buf)-1<t_bend){
		t_bend=bb_monkeybuffer_GetBufferLength(t_dest.m_buf)-1;
	}
	for(var t_i=t_begin;t_i<=t_bend;t_i=t_i+4){
		t_dest.m_buf.PokeInt(t_i,t_src.m_buf.PeekInt(t_i));
	}
	return t_dest;
}
function bb_monkeybuffer_CopyShortBuffer(t_src,t_dest){
	if(t_src==null || t_src.m_buf==null){
		return t_dest;
	}
	var t_size=t_src.p_Length();
	if(t_dest.p_Length()<t_size){
		t_size=t_dest.p_Length();
	}
	for(var t_i=0;t_i<=t_size-1;t_i=t_i+1){
		t_dest.p_Poke2(t_i,t_src.p_Peek(t_i));
	}
	return t_dest;
}
function c_TSprite(){
	c_TMesh.call(this);
	this.m_pixel_scale=new_number_array(2);
	this.m_mat_sp=c_Matrix.m_new.call(new c_Matrix);
	this.m_scale_x=1.0;
	this.m_scale_y=1.0;
	this.m_view_mode=1;
	this.m_angle=.0;
	this.m_handle_x=.0;
	this.m_handle_y=.0;
	this.implments={c_IRenderUpdate:1};
}
c_TSprite.prototype=extend_class(c_TMesh);
c_TSprite.m_new=function(){
	c_TMesh.m_new.call(this);
	this.m_is_sprite=true;
	return this;
}
c_TSprite.prototype.p_ScaleSprite=function(t_s_x,t_s_y){
	this.m_scale_x=t_s_x;
	this.m_scale_y=t_s_y;
	if((this.m_parent)!=null){
		this.m_gsx=t_s_x*this.m_parent.m_gsx;
		this.m_gsy=t_s_y*this.m_parent.m_gsy;
		this.m_gsz=this.m_gsx;
	}else{
		this.m_gsx=t_s_x;
		this.m_gsy=t_s_y;
		this.m_gsz=this.m_gsx;
	}
	return (this);
}
c_TSprite.prototype.p_ScaleEntity=function(t_x,t_y,t_z,t_glob){
	this.p_ScaleSprite(t_x,t_y);
	return (this);
}
c_TSprite.m_temp_mat=null;
c_TSprite.prototype.p_Update2=function(t_cam){
	if(this.m_view_mode!=2){
		var t_x=this.m_mat.m_grid[3][0];
		var t_y=this.m_mat.m_grid[3][1];
		var t_z=this.m_mat.m_grid[3][2];
		c_TSprite.m_temp_mat.p_Overwrite(t_cam.m_mat);
		c_TSprite.m_temp_mat.m_grid[3][0]=t_x;
		c_TSprite.m_temp_mat.m_grid[3][1]=t_y;
		c_TSprite.m_temp_mat.m_grid[3][2]=t_z;
		this.m_mat_sp.p_Overwrite(c_TSprite.m_temp_mat);
		if(this.m_angle!=0.0){
			this.m_mat_sp.p_RotateRoll(this.m_angle);
		}
		if(this.m_scale_x!=1.0 || this.m_scale_y!=1.0){
			this.m_mat_sp.p_Scale(this.m_scale_x,this.m_scale_y,1.0);
		}
		if(this.m_handle_x!=0.0 || this.m_handle_y!=0.0){
			this.m_mat_sp.p_Translate(-this.m_handle_x,-this.m_handle_y,0.0);
		}
	}else{
		this.m_mat_sp.p_Overwrite(this.m_mat);
		if(this.m_scale_x!=1.0 || this.m_scale_y!=1.0){
			this.m_mat_sp.p_Scale(this.m_scale_x,this.m_scale_y,1.0);
		}
	}
	return 0;
}
function c_TText(){
	c_TSprite.call(this);
	this.m_text="";
	this.m_length=0;
	this.m_cam=null;
	this.m_char_pixels=.0;
	this.m_font_file="";
	this.m_orig_width=.0;
	this.m_orig_height=.0;
	this.m_surf=null;
	this.m_char_rows=0;
	this.m_pixel_ratio=.0;
	this.m_char_uvwidth=.0;
	this.m_padding=0;
	this.m_mode=true;
	this.m_old_text="";
	this.implments={c_IRenderUpdate:1};
}
c_TText.prototype=extend_class(c_TSprite);
c_TText.m_new=function(){
	c_TSprite.m_new.call(this);
	return this;
}
c_TText.m_mask_color=0;
c_TText.prototype.p_AddChar=function(t_char,t_num,t_x,t_y,t_offset){
	var t_uv=((t_char)-32.0)*this.m_char_uvwidth+((t_char)-32.0)*(this.m_padding);
	var t_uv2=0.9999;
	if(t_char==32){
		t_uv=0.0;
		t_uv2=0.0001;
	}
	if(t_uv<0.0){
		t_uv=0.0;
		t_uv2=0.0001;
	}
	var t_kern=0.3*t_x+t_offset;
	this.m_surf.p_AddVertex(0.0+t_x-t_kern,0.0+t_y,0.0,t_uv,t_uv2,0.0);
	this.m_surf.p_AddVertex(0.0+t_x-t_kern,1.0+t_y,0.0,t_uv,0.0001,0.0);
	this.m_surf.p_AddVertex(1.0+t_x-t_kern,1.0+t_y,0.0,t_uv+this.m_char_uvwidth,0.0001,0.0);
	this.m_surf.p_AddVertex(1.0+t_x-t_kern,0.0+t_y,0.0,t_uv+this.m_char_uvwidth,t_uv2,0.0);
	var t_v=t_num*4;
	this.m_surf.p_AddTriangle(0+t_v,1+t_v,2+t_v);
	this.m_surf.p_AddTriangle(0+t_v,2+t_v,3+t_v);
	return 0;
}
c_TText.prototype.p_AdjustChar=function(t_char,t_num,t_x,t_y){
	var t_uv=((t_char)-32.0)*this.m_char_uvwidth+((t_char)-32.0)*(this.m_padding);
	var t_uv2=0.9999;
	if(t_char==32){
		t_uv=0.0;
		t_uv2=0.0001;
	}
	if(t_uv<0.0){
		t_uv=0.0;
		t_uv2=0.0001;
	}
	var t_kern=0.3*t_x;
	var t_v=t_num*4;
	this.m_surf.p_VertexTexCoords(t_v+0,t_uv,t_uv2,0.0,0);
	this.m_surf.p_VertexTexCoords(t_v+1,t_uv,0.0001,0.0,0);
	this.m_surf.p_VertexTexCoords(t_v+2,t_uv+this.m_char_uvwidth,0.0001,0.0,0);
	this.m_surf.p_VertexTexCoords(t_v+3,t_uv+this.m_char_uvwidth,t_uv2,0.0,0);
	return 0;
}
c_TText.prototype.p_SetText=function(t_str,t_x,t_y,t_z,t_align){
	var t_resurf=0;
	if(this.m_mode==false){
		this.p_PositionEntity(t_x,t_y,t_z,0);
	}
	if(t_str==this.m_old_text || t_str==""){
		return 0;
	}
	if(t_str.length!=this.m_old_text.length){
		if((this.m_surf)!=null){
			this.m_surf.p_ClearSurface(true,true,false);
		}else{
			this.m_surf=this.p_CreateSurface(null);
		}
		t_resurf=1;
	}
	this.m_old_text=t_str;
	var t_nl=-1;
	var t_xx=0;
	var t_total=0;
	var t_offset=0.0;
	if(t_align==1){
		t_offset=(t_str.length)*0.15;
	}
	for(var t_i=0;t_i<=t_str.length-1;t_i=t_i+1){
		if(t_str.charCodeAt(t_i)==13 || t_str.charCodeAt(t_i)==10){
			t_nl=t_nl-1;
			t_xx=0;
			continue;
		}
		if((t_resurf)!=0){
			this.p_AddChar(t_str.charCodeAt(t_i),t_total,(t_xx),(t_nl),t_offset);
		}else{
			this.p_AdjustChar(t_str.charCodeAt(t_i),t_total,(t_xx),(t_nl));
		}
		t_xx+=1;
		t_total+=1;
	}
	if((t_resurf)!=0){
		this.m_surf.p_CropSurfaceBuffers();
	}
	return 0;
}
c_TText.m_CreateText=function(t_camx,t_str,t_font,t_num_chars,t_c_pixels,t_pad,t_mode){
	var t_tt=c_TText.m_new.call(new c_TText);
	t_tt.m_entity_link=c_TEntity.m_entity_list.p_EntityListAdd(t_tt);
	t_tt.m_text=t_str;
	t_tt.m_length=t_str.length;
	t_tt.m_cam=t_camx;
	t_tt.m_classname="TextSprite";
	t_tt.m_is_sprite=true;
	t_tt.m_char_pixels=(t_c_pixels);
	if(t_font!=""){
		t_tt.m_font_file=t_font;
	}else{
		t_tt.m_font_file="mojo_font.png";
	}
	c_TTexture.m_useGlobalResizeSmooth=false;
	var t_pixmap=c_TPixmap.m_LoadPixmap(t_tt.m_font_file).p_Copy();
	t_tt.m_orig_width=(t_pixmap.m_width);
	t_tt.m_orig_height=(t_pixmap.m_height);
	if(t_pixmap.m_height==0){
		print("Font file not found.");
		return t_tt;
	}
	t_pixmap.p_MaskPixmap(c_TText.m_mask_color&255,(c_TText.m_mask_color&65280)>>8,(c_TText.m_mask_color&16711680)>>16);
	var t_tex=c_TTexture.m_LoadTexture2(t_pixmap,3,null);
	c_TTexture.m_useGlobalResizeSmooth=true;
	t_tex.m_is_font=true;
	t_tex.m_flags=50;
	t_tex.p_TextureBlend(2);
	t_tt.p_EntityShininess(0.0);
	t_tt.m_surf=t_tt.p_CreateSurface(null);
	t_tt.p_EntityTexture(t_tex,0,0);
	t_tt.m_char_rows=((Math.ceil(((t_c_pixels*t_num_chars)+(t_num_chars)*(t_pad*2.0))/t_tt.m_orig_width))|0);
	t_tt.m_pixel_ratio=(t_tex.m_width)/t_tt.m_orig_width;
	var t_temp_chars_per_row=((Math.floor((t_num_chars/t_tt.m_char_rows)|0))|0);
	t_tt.m_char_uvwidth=t_tt.m_char_pixels*t_tt.m_pixel_ratio/(t_tex.m_width);
	t_tt.m_padding=((t_tt.m_pixel_ratio*t_pad)|0);
	if(t_tt.m_parent!=null){
		t_tt.m_mat.p_Overwrite(t_tt.m_parent.m_mat);
		t_tt.p_UpdateMat(false);
	}else{
		t_tt.p_UpdateMat(true);
	}
	t_tt.p_EntityFX(33);
	t_tt.p_SetText(t_str,0.0,0.0,0.0,0);
	return t_tt;
}
c_TText.m_CreateText2D=function(t_camx,t_str,t_font,t_num_chars,t_c_pixels,t_pad){
	var t_tt=c_TText.m_CreateText(t_camx,t_str,t_font,t_num_chars,t_c_pixels,(t_pad),true);
	t_tt.p_HideEntity();
	return t_tt;
}
function c_TTextureFilter(){
	Object.call(this);
	this.m_text="";
	this.m_flags=0;
}
c_TTextureFilter.m_filter_list=null;
c_TTextureFilter.m_new=function(){
	return this;
}
function c_List8(){
	Object.call(this);
	this.m__head=(c_HeadNode8.m_new.call(new c_HeadNode8));
}
c_List8.m_new=function(){
	return this;
}
c_List8.prototype.p_AddLast8=function(t_data){
	return c_Node10.m_new.call(new c_Node10,this.m__head,this.m__head.m__pred,t_data);
}
c_List8.m_new2=function(t_data){
	var t_=t_data;
	var t_2=0;
	while(t_2<t_.length){
		var t_t=t_[t_2];
		t_2=t_2+1;
		this.p_AddLast8(t_t);
	}
	return this;
}
c_List8.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator4.m_new.call(new c_Enumerator4,this);
}
function c_Node10(){
	Object.call(this);
	this.m__succ=null;
	this.m__pred=null;
	this.m__data=null;
}
c_Node10.m_new=function(t_succ,t_pred,t_data){
	this.m__succ=t_succ;
	this.m__pred=t_pred;
	this.m__succ.m__pred=this;
	this.m__pred.m__succ=this;
	this.m__data=t_data;
	return this;
}
c_Node10.m_new2=function(){
	return this;
}
function c_HeadNode8(){
	c_Node10.call(this);
}
c_HeadNode8.prototype=extend_class(c_Node10);
c_HeadNode8.m_new=function(){
	c_Node10.m_new2.call(this);
	this.m__succ=(this);
	this.m__pred=(this);
	return this;
}
function c_Enumerator4(){
	Object.call(this);
	this.m__list=null;
	this.m__curr=null;
}
c_Enumerator4.m_new=function(t_list){
	this.m__list=t_list;
	this.m__curr=t_list.m__head.m__succ;
	return this;
}
c_Enumerator4.m_new2=function(){
	return this;
}
c_Enumerator4.prototype.p_HasNext=function(){
	while(this.m__curr.m__succ.m__pred!=this.m__curr){
		this.m__curr=this.m__curr.m__succ;
	}
	return this.m__curr!=this.m__list.m__head;
}
c_Enumerator4.prototype.p_NextObject=function(){
	var t_data=this.m__curr.m__data;
	this.m__curr=this.m__curr.m__succ;
	return t_data;
}
function c_List9(){
	Object.call(this);
	this.m__head=(c_HeadNode9.m_new.call(new c_HeadNode9));
}
c_List9.m_new=function(){
	return this;
}
c_List9.prototype.p_AddLast9=function(t_data){
	return c_Node11.m_new.call(new c_Node11,this.m__head,this.m__head.m__pred,t_data);
}
c_List9.m_new2=function(t_data){
	var t_=t_data;
	var t_2=0;
	while(t_2<t_.length){
		var t_t=t_[t_2];
		t_2=t_2+1;
		this.p_AddLast9(t_t);
	}
	return this;
}
c_List9.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator5.m_new.call(new c_Enumerator5,this);
}
function c_Node11(){
	Object.call(this);
	this.m__succ=null;
	this.m__pred=null;
	this.m__data=null;
}
c_Node11.m_new=function(t_succ,t_pred,t_data){
	this.m__succ=t_succ;
	this.m__pred=t_pred;
	this.m__succ.m__pred=this;
	this.m__pred.m__succ=this;
	this.m__data=t_data;
	return this;
}
c_Node11.m_new2=function(){
	return this;
}
c_Node11.prototype.p_Remove=function(){
	this.m__succ.m__pred=this.m__pred;
	this.m__pred.m__succ=this.m__succ;
	return 0;
}
function c_HeadNode9(){
	c_Node11.call(this);
}
c_HeadNode9.prototype=extend_class(c_Node11);
c_HeadNode9.m_new=function(){
	c_Node11.m_new2.call(this);
	this.m__succ=(this);
	this.m__pred=(this);
	return this;
}
function c_Enumerator5(){
	Object.call(this);
	this.m__list=null;
	this.m__curr=null;
}
c_Enumerator5.m_new=function(t_list){
	this.m__list=t_list;
	this.m__curr=t_list.m__head.m__succ;
	return this;
}
c_Enumerator5.m_new2=function(){
	return this;
}
c_Enumerator5.prototype.p_HasNext=function(){
	while(this.m__curr.m__succ.m__pred!=this.m__curr){
		this.m__curr=this.m__curr.m__succ;
	}
	return this.m__curr!=this.m__list.m__head;
}
c_Enumerator5.prototype.p_NextObject=function(){
	var t_data=this.m__curr.m__data;
	this.m__curr=this.m__curr.m__succ;
	return t_data;
}
function c_Stack3(){
	Object.call(this);
	this.m_data=[];
	this.m_length=0;
}
c_Stack3.m_new=function(){
	return this;
}
c_Stack3.m_new2=function(t_data){
	this.m_data=t_data.slice(0);
	this.m_length=t_data.length;
	return this;
}
c_Stack3.prototype.p_Push7=function(t_value){
	if(this.m_length==this.m_data.length){
		this.m_data=resize_object_array(this.m_data,this.m_length*2+10);
	}
	this.m_data[this.m_length]=t_value;
	this.m_length+=1;
}
c_Stack3.prototype.p_Push8=function(t_values,t_offset,t_count){
	for(var t_i=0;t_i<t_count;t_i=t_i+1){
		this.p_Push7(t_values[t_offset+t_i]);
	}
}
c_Stack3.prototype.p_Push9=function(t_values,t_offset){
	this.p_Push8(t_values,t_offset,t_values.length-t_offset);
}
c_Stack3.m_NIL=null;
c_Stack3.prototype.p_Length2=function(t_newlength){
	if(t_newlength<this.m_length){
		for(var t_i=t_newlength;t_i<this.m_length;t_i=t_i+1){
			this.m_data[t_i]=c_Stack3.m_NIL;
		}
	}else{
		if(t_newlength>this.m_data.length){
			this.m_data=resize_object_array(this.m_data,bb_math_Max(this.m_length*2+10,t_newlength));
		}
	}
	this.m_length=t_newlength;
}
c_Stack3.prototype.p_Length=function(){
	return this.m_length;
}
c_Stack3.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator10.m_new.call(new c_Enumerator10,this);
}
c_Stack3.prototype.p_Clear=function(){
	for(var t_i=0;t_i<this.m_length;t_i=t_i+1){
		this.m_data[t_i]=c_Stack3.m_NIL;
	}
	this.m_length=0;
}
function c_TextureStack(){
	c_Stack3.call(this);
}
c_TextureStack.prototype=extend_class(c_Stack3);
c_TextureStack.m_new=function(){
	c_Stack3.m_new.call(this);
	return this;
}
function bb_input_KeyDown(t_key){
	return ((bb_input_device.p_KeyDown(t_key))?1:0);
}
function bb_input_TouchDown(t_index){
	return ((bb_input_device.p_KeyDown(384+t_index))?1:0);
}
function bb_input_TouchX(t_index){
	return bb_input_device.p_TouchX(t_index);
}
function bb_input_TouchY(t_index){
	return bb_input_device.p_TouchY(t_index);
}
function c_CollisionInfo(){
	Object.call(this);
	this.m_tform=c_TransformMat.m_new.call(new c_TransformMat);
	this.m_src_radius=.0;
	this.m_dst_radius=.0;
	this.m_planes=[c_Plane.m_new4.call(new c_Plane),c_Plane.m_new4.call(new c_Plane),c_Plane.m_new4.call(new c_Plane),c_Plane.m_new4.call(new c_Plane)];
	this.m_col_points=[c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0),c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0),c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0),c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0)];
	this.m_src_divSphere=c_DivSphereInfo.m_new.call(new c_DivSphereInfo);
	this.m_dst_divSphere=c_DivSphereInfo.m_new.call(new c_DivSphereInfo);
	this.m_dst_entity=null;
	this.m_hitDivOffset=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_hitDivRadius=0.0;
	this.m_vec_a=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_vec_b=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_vec_c=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_hits=0;
	this.m_n_hit=0;
	this.m_ax=.0;
	this.m_ay=.0;
	this.m_az=.0;
	this.m_bx=.0;
	this.m_by=.0;
	this.m_bz=.0;
	this.m_dv=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_sv=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_panic=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_radii=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_coll_line=c_Line.m_new.call(new c_Line);
	this.m_ray_length=.0;
	this.m_ray_dir=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_ray_center=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_y_scale=.0;
	this.m_inv_y_scale=.0;
	this.m_y_tform=c_TransformMat.m_new.call(new c_TransformMat);
	this.m_col_passes=0;
	this.m_dst_pos=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_coll_method=0;
	this.m_inv_scale=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_renew2=c_Matrix.m_new.call(new c_Matrix);
	this.m_tf_scale=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_tf_radius=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_tf_line=c_Line.m_new.call(new c_Line);
	this.m_tf_offset=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_tf_box=c_Box.m_new.call(new c_Box);
	this.m_mesh_coll=null;
}
c_CollisionInfo.m_t_mat=null;
c_CollisionInfo.m_new=function(){
	c_CollisionInfo.m_t_mat=c_Matrix.m_new.call(new c_Matrix);
	this.m_tform=c_TransformMat.m_new.call(new c_TransformMat);
	return this;
}
c_CollisionInfo.m_col_info=null;
c_CollisionInfo.prototype.p_Clear=function(){
	this.m_src_radius=0.0;
	this.m_dst_radius=0.0;
	this.m_planes[0].p_Update9(0.0,0.0,0.0,0.0);
	this.m_planes[1].p_Update9(0.0,0.0,0.0,0.0);
	this.m_planes[2].p_Update9(0.0,0.0,0.0,0.0);
	this.m_planes[3].p_Update9(0.0,0.0,0.0,0.0);
	this.m_col_points[0].p_Update(0.0,0.0,0.0);
	this.m_col_points[1].p_Update(0.0,0.0,0.0);
	this.m_col_points[2].p_Update(0.0,0.0,0.0);
	this.m_col_points[3].p_Update(0.0,0.0,0.0);
	this.m_src_divSphere.p_Clear();
	this.m_dst_divSphere.p_Clear();
	this.m_dst_entity=null;
}
c_CollisionInfo.prototype.p_UpdateSourceSphere=function(t_ent){
	if(t_ent.m_collision.m_radius_x==0.0){
		var t_sc=bb_math_Max2(bb_math_Max2(bb_math_Abs2(t_ent.m_gsx),bb_math_Abs2(t_ent.m_gsy)),bb_math_Abs2(t_ent.m_gsz));
		this.m_src_radius=t_ent.p_EntityRadius(0.0,0.0)*t_sc;
	}
	this.m_src_divSphere.m_offset.p_Overwrite2(0.0,0.0,0.0);
	this.m_hitDivOffset.p_Overwrite2(0.0,0.0,0.0);
	this.m_hitDivRadius=0.0;
	return 0;
}
c_CollisionInfo.prototype.p_ClearHitPlanes=function(){
	this.m_planes[0].p_Update9(0.0,0.0,0.0,0.0);
	this.m_planes[1].p_Update9(0.0,0.0,0.0,0.0);
	this.m_planes[2].p_Update9(0.0,0.0,0.0,0.0);
	this.m_planes[3].p_Update9(0.0,0.0,0.0,0.0);
	this.m_col_points[0].p_Update(0.0,0.0,0.0);
	this.m_col_points[1].p_Update(0.0,0.0,0.0);
	this.m_col_points[2].p_Update(0.0,0.0,0.0);
	this.m_col_points[3].p_Update(0.0,0.0,0.0);
	this.m_hits=0;
	this.m_n_hit=0;
	this.m_ax=1.0;
	this.m_ay=1.0;
	this.m_az=1.0;
	this.m_bx=1.0;
	this.m_by=1.0;
	this.m_bz=1.0;
}
c_CollisionInfo.prototype.p_UpdateRay=function(t_sv2,t_dv2,t_radii2){
	c_CollisionInfo.m_t_mat.p_LoadIdentity();
	this.m_tform.m_m.p_LoadIdentity();
	this.m_dv.p_Overwrite3(t_dv2);
	this.m_sv.p_Overwrite3(t_sv2);
	this.m_panic.p_Overwrite3(t_sv2);
	this.m_radii.p_Overwrite3(t_radii2);
	this.m_src_radius=this.m_radii.m_x;
	this.m_coll_line.p_Update3(this.m_sv.m_x,this.m_sv.m_y,this.m_sv.m_z,this.m_dv.m_x-this.m_sv.m_x,this.m_dv.m_y-this.m_sv.m_y,this.m_dv.m_z-this.m_sv.m_z);
	this.m_ray_length=this.m_coll_line.m_d.p_Length();
	if(this.m_ray_length>0.00001){
		this.m_ray_dir=this.m_coll_line.p_Multiply5(1.0/this.m_ray_length);
	}else{
		this.m_ray_dir=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	}
	this.m_ray_center.p_Update(this.m_coll_line.m_o.m_x+this.m_coll_line.m_d.m_x*0.5,this.m_coll_line.m_o.m_y+this.m_coll_line.m_d.m_y*0.5,this.m_coll_line.m_o.m_z+this.m_coll_line.m_d.m_z*0.5);
	this.m_y_scale=1.0;
	this.m_inv_y_scale=1.0;
	this.m_y_tform.m_m.m_grid[1][1]=1.0;
	return 0;
}
c_CollisionInfo.m_starttime=0;
c_CollisionInfo.prototype.p_UpdateDestShape=function(t_ent){
	if(!t_ent.m_collision.m_updated_shape){
		var t_sc=bb_math_Max2(bb_math_Max2(bb_math_Abs2(t_ent.m_gsx),bb_math_Abs2(t_ent.m_gsy)),bb_math_Abs2(t_ent.m_gsz));
		if(t_ent.m_collision.m_radius_x!=0.0){
			this.m_dst_radius=t_ent.m_collision.m_radius_x*t_sc;
		}else{
			this.m_dst_radius=t_ent.p_EntityRadius(0.0,0.0)*t_sc;
		}
		if(t_ent.m_collision.m_box_w==0.0){
			t_ent.p_EntityBox(0.0,0.0,0.0,0.0,0.0,0.0);
		}
		this.m_ax=t_ent.m_collision.m_box_x*t_sc;
		this.m_ay=t_ent.m_collision.m_box_y*t_sc;
		this.m_az=t_ent.m_collision.m_box_z*t_sc;
		this.m_bx=t_ent.m_collision.m_box_x+t_ent.m_collision.m_box_w*t_sc;
		this.m_by=t_ent.m_collision.m_box_y+t_ent.m_collision.m_box_h*t_sc;
		this.m_bz=t_ent.m_collision.m_box_z+t_ent.m_collision.m_box_d*t_sc;
		t_ent.m_collision.m_updated_shape=true;
		t_ent.m_collision.m_sa=[this.m_ax,this.m_ay,this.m_az,this.m_bx,this.m_by,this.m_bz];
		t_ent.m_collision.m_old_dst_radius=this.m_dst_radius;
	}else{
		this.m_dst_radius=t_ent.m_collision.m_old_dst_radius;
		this.m_ax=t_ent.m_collision.m_sa[0];
		this.m_ay=t_ent.m_collision.m_sa[1];
		this.m_az=t_ent.m_collision.m_sa[2];
		this.m_bx=t_ent.m_collision.m_sa[3];
		this.m_by=t_ent.m_collision.m_sa[4];
		this.m_bz=t_ent.m_collision.m_sa[5];
	}
	this.m_dst_divSphere.m_offset.p_Overwrite2(0.0,0.0,0.0);
	this.m_dst_pos.p_Update(t_ent.m_mat.m_grid[3][0],t_ent.m_mat.m_grid[3][1],-t_ent.m_mat.m_grid[3][2]);
	return 0;
}
c_CollisionInfo.m_nullVec=null;
c_CollisionInfo.prototype.p_CreateCollLine=function(t_li,t_ent,t_mat,t_offset2){
	if(this.m_coll_method==2 || this.m_coll_method==3 || this.m_coll_method==5){
		this.m_tf_offset.p_Overwrite2(-t_ent.m_mat.m_grid[3][0],-t_ent.m_mat.m_grid[3][1],t_ent.m_mat.m_grid[3][2]);
		t_li.p_Update4(this.m_coll_line);
		c_Vec3.m_Add(t_li.m_o,this.m_tf_offset,t_li.m_o);
		c_Vec3.m_Add(t_li.m_o,this.m_src_divSphere.m_offset,t_li.m_o);
		c_Vec3.m_Multiply3(t_li.m_o,this.m_tform.m_m,t_li.m_o);
		c_Vec3.m_Multiply3(t_li.m_d,this.m_tform.m_m,t_li.m_d);
	}else{
		this.m_tf_offset.p_Overwrite2(t_ent.m_mat.m_grid[3][0],t_ent.m_mat.m_grid[3][1],-t_ent.m_mat.m_grid[3][2]);
		c_Vec3.m_Add(this.m_src_divSphere.m_offset,this.m_coll_line.m_o,t_li.m_o);
		t_li.m_d.p_Overwrite3(this.m_coll_line.m_d);
	}
}
c_CollisionInfo.prototype.p_CollisionBoxSetup=function(t_ent,t_radius,t_coll_obj){
	this.m_tf_box.p_Clear();
	this.m_tf_box.m_a.p_Overwrite2(t_ent.m_collision.m_box_x,t_ent.m_collision.m_box_y,t_ent.m_collision.m_box_z);
	this.m_tf_box.p_Update10(c_Vector.m_new.call(new c_Vector,t_ent.m_collision.m_box_x+t_ent.m_collision.m_box_w,t_ent.m_collision.m_box_y+t_ent.m_collision.m_box_h,t_ent.m_collision.m_box_z+t_ent.m_collision.m_box_d));
	this.m_tf_box.p_Scale2(this.m_tf_scale);
	return 0;
}
c_CollisionInfo.prototype.p_CollisionSphereSetup=function(t_ent,t_radius,t_coll_obj){
	if(this.m_dst_entity!=t_ent){
		this.m_dst_divSphere.p_RotateDivSpheres(t_ent,this,true);
	}
	this.m_dst_entity=t_ent;
	return 0;
}
c_CollisionInfo.m_t_vec=null;
c_CollisionInfo.prototype.p_CreateCollBox=function(t_box,t_li,t_radius,t_scale){
	t_box.p_Clear();
	t_box.p_Update10(t_li.m_o);
	t_radius=t_radius*1.001;
	var t_delta=c_CollisionInfo.m_t_vec;
	t_delta.p_Update(t_li.m_o.m_x+t_li.m_d.m_x+t_radius,t_li.m_o.m_y+t_li.m_d.m_y+t_radius,t_li.m_o.m_z+t_li.m_d.m_z+t_radius);
	t_box.p_Update10(t_delta);
	t_delta.p_Update(t_li.m_o.m_x+t_li.m_d.m_x-t_radius,t_li.m_o.m_y+t_li.m_d.m_y-t_radius,t_li.m_o.m_z+t_li.m_d.m_z-t_radius);
	t_box.p_Update10(t_delta);
	t_box.m_a.m_x*=t_scale.m_x;
	t_box.m_a.m_y*=t_scale.m_y;
	t_box.m_a.m_z*=t_scale.m_z;
	t_box.m_b.m_x*=t_scale.m_x;
	t_box.m_b.m_y*=t_scale.m_y;
	t_box.m_b.m_z*=t_scale.m_z;
}
c_CollisionInfo.prototype.p_CollisionTriangleSetup=function(t_ent,t_radius,t_coll_obj){
	var t_mesh=object_downcast((t_ent),c_TMesh);
	if(t_mesh!=null){
		this.m_mesh_coll=t_mesh.m_col_tree.p_CreateMeshTree(t_mesh,8);
	}
	this.p_CreateCollBox(this.m_tf_box,this.m_tf_line,t_radius,this.m_inv_scale);
	if(t_radius==0.0){
		this.m_tf_radius.m_x=0.0;
		this.m_mesh_coll.p_RayBoxSetup(c_Line.m_new2.call(new c_Line,this.m_tf_line.m_o.p_Multiply2(this.m_inv_scale),this.m_tf_line.m_d.p_Multiply2(this.m_inv_scale)));
	}
	return 0;
}
c_CollisionInfo.prototype.p_CollisionAABBSetup=function(t_ent,t_radius,t_coll_obj){
	this.m_tf_line=c_Line.m_new2.call(new c_Line,this.m_coll_line.m_o,this.m_coll_line.m_d);
	this.m_tf_box.p_Clear();
	var t_xx=t_ent.m_mat.m_grid[3][0];
	var t_yy=t_ent.m_mat.m_grid[3][1];
	var t_zz=-t_ent.m_mat.m_grid[3][2];
	this.m_tf_box.m_a.p_Overwrite2(t_ent.m_collision.m_box_x,t_ent.m_collision.m_box_y,t_ent.m_collision.m_box_z);
	this.m_tf_box.p_Update10(c_Vector.m_new.call(new c_Vector,t_ent.m_collision.m_box_x+t_ent.m_collision.m_box_w,t_ent.m_collision.m_box_y+t_ent.m_collision.m_box_h,t_ent.m_collision.m_box_z+t_ent.m_collision.m_box_d));
	this.m_tf_box.p_Scale2(this.m_tf_scale);
	this.m_tf_box.m_a.m_x+=t_xx-t_radius;
	this.m_tf_box.m_a.m_y+=t_yy-t_radius;
	this.m_tf_box.m_a.m_z+=t_zz-t_radius;
	this.m_tf_box.m_b.m_x+=t_xx+t_radius;
	this.m_tf_box.m_b.m_y+=t_yy+t_radius;
	this.m_tf_box.m_b.m_z+=t_zz+t_radius;
	return 0;
}
c_CollisionInfo.prototype.p_CollisionSetup=function(t_ent,t_coll_method,t_coll_obj,t_offset2,t_pass2){
	if(t_coll_method!=1 && t_coll_method!=4 && t_pass2==false){
		var t_ex=.0;
		var t_ey=.0;
		var t_ez=.0;
		if(t_ent.m_collision.m_old_gsx==t_ent.m_gsx && t_ent.m_collision.m_old_gsy==t_ent.m_gsy && t_ent.m_collision.m_old_gsz==t_ent.m_gsz){
			t_ex=t_ent.m_collision.m_old_igsx;
			t_ey=t_ent.m_collision.m_old_igsy;
			t_ez=t_ent.m_collision.m_old_igsz;
		}else{
			t_ex=1.0/t_ent.m_gsx;
			t_ey=1.0/t_ent.m_gsy;
			t_ez=1.0/t_ent.m_gsz;
			t_ent.m_collision.m_old_gsx=t_ent.m_gsx;
			t_ent.m_collision.m_old_gsy=t_ent.m_gsy;
			t_ent.m_collision.m_old_gsz=t_ent.m_gsz;
			t_ent.m_collision.m_old_igsx=t_ex;
			t_ent.m_collision.m_old_igsy=t_ey;
			t_ent.m_collision.m_old_igsz=t_ez;
		}
		this.m_inv_scale.p_Overwrite2(t_ex,t_ey,t_ez);
		this.m_tform.m_m.m_grid[0]=[t_ent.m_mat.m_grid[0][0]*t_ex,t_ent.m_mat.m_grid[1][0]*t_ey,-t_ent.m_mat.m_grid[2][0]*t_ez,0.0];
		this.m_tform.m_m.m_grid[1]=[t_ent.m_mat.m_grid[0][1]*t_ex,t_ent.m_mat.m_grid[1][1]*t_ey,-t_ent.m_mat.m_grid[2][1]*t_ez,0.0];
		this.m_tform.m_m.m_grid[2]=[-t_ent.m_mat.m_grid[0][2]*t_ex,-t_ent.m_mat.m_grid[1][2]*t_ey,t_ent.m_mat.m_grid[2][2]*t_ez,0.0];
		this.m_renew2.m_grid[0]=[this.m_tform.m_m.m_grid[0][0],this.m_tform.m_m.m_grid[1][0],this.m_tform.m_m.m_grid[2][0],0.0];
		this.m_renew2.m_grid[1]=[this.m_tform.m_m.m_grid[0][1],this.m_tform.m_m.m_grid[1][1],this.m_tform.m_m.m_grid[2][1],0.0];
		this.m_renew2.m_grid[2]=[this.m_tform.m_m.m_grid[0][2],this.m_tform.m_m.m_grid[1][2],this.m_tform.m_m.m_grid[2][2],0.0];
		if(this.m_y_scale!=1.0){
			this.m_tform=this.m_y_tform.p_Multiply8(this.m_tform);
		}
	}
	this.m_tf_scale.p_Overwrite2(t_ent.m_gsx,t_ent.m_gsy,t_ent.m_gsz);
	this.m_tf_radius.p_Overwrite2(this.m_src_radius,this.m_src_radius,this.m_src_radius);
	this.p_CreateCollLine(this.m_tf_line,t_ent,this.m_tform.m_m,t_offset2);
	if(t_coll_method==3){
		this.p_CollisionBoxSetup(t_ent,this.m_src_radius,t_coll_obj);
	}else{
		if(t_coll_method==1){
			this.p_CollisionSphereSetup(t_ent,this.m_src_radius,t_coll_obj);
		}else{
			if(t_coll_method==2){
				this.p_CollisionTriangleSetup(t_ent,this.m_src_radius,t_coll_obj);
			}else{
				if(t_coll_method==5){
					this.p_CollisionTriangleSetup(t_ent,this.m_src_radius,t_coll_obj);
				}else{
					if(t_coll_method==4){
						this.p_CollisionAABBSetup(t_ent,this.m_src_radius,t_coll_obj);
					}
				}
			}
		}
	}
	return 0;
}
c_CollisionInfo.prototype.p_CollisionDetect=function(t_coll_obj){
	var t_res=0;
	var t_1=this.m_coll_method;
	if(t_1==1){
		if(this.m_dst_divSphere.m_num>1){
			var t_hit=0;
			for(var t_i=0;t_i<=this.m_dst_divSphere.m_num-1;t_i=t_i+1){
				this.m_dst_radius=this.m_dst_divSphere.m_rad[t_i];
				t_hit+=t_coll_obj.p_SphereCollide(this.m_tf_line,this.m_src_radius,this.m_tf_offset.p_Add2(this.m_dst_divSphere.m_pos[t_i].m_x,this.m_dst_divSphere.m_pos[t_i].m_y,this.m_dst_divSphere.m_pos[t_i].m_z),this.m_dst_divSphere.m_rad[t_i],c_CollisionInfo.m_nullVec);
			}
			if(t_hit>0){
				t_res=1;
			}
		}else{
			t_res=t_coll_obj.p_SphereCollide(this.m_tf_line,this.m_src_radius,this.m_tf_offset,this.m_dst_radius,c_CollisionInfo.m_nullVec);
		}
		if((t_res)!=0){
		}
	}else{
		if(t_1==2){
			if((this.m_mesh_coll.p_CollideNodeAABB(this.m_tf_box,this.m_tf_radius,t_coll_obj,this.m_mesh_coll.m_tree,false))!=0){
				t_res=this.m_mesh_coll.p_TriNodeCollide(this.m_tf_box,this.m_tf_line,this.m_tf_radius,t_coll_obj,this.m_tf_scale,false);
				if((t_res)!=0){
					t_coll_obj.m_normal=this.m_renew2.p_Multiply2(t_coll_obj.m_normal);
					t_coll_obj.m_col_coords=this.m_renew2.p_Multiply2(t_coll_obj.m_col_coords).p_Subtract(this.m_tf_offset);
				}
			}
		}else{
			if(t_1==5){
				if((this.m_mesh_coll.p_CollideNodeAABB(this.m_tf_box,this.m_tf_radius,t_coll_obj,this.m_mesh_coll.m_tree,false))!=0){
					t_res=this.m_mesh_coll.p_TriNodeCollide(this.m_tf_box,this.m_tf_line,this.m_tf_radius,t_coll_obj,this.m_tf_scale,true);
					if((t_res)!=0){
						t_coll_obj.m_normal=this.m_renew2.p_Multiply2(t_coll_obj.m_normal);
						t_coll_obj.m_col_coords=this.m_renew2.p_Multiply2(t_coll_obj.m_col_coords).p_Subtract(this.m_tf_offset);
					}
				}
			}else{
				if(t_1==3){
					if((t_coll_obj.p_SphereBox(this.m_tf_line,this.m_tf_radius,this.m_tf_box,this.m_tf_scale))!=0){
						t_coll_obj.m_normal=this.m_renew2.p_Multiply2(t_coll_obj.m_normal);
						t_res=1;
						t_coll_obj.m_col_coords=this.m_renew2.p_Multiply2(t_coll_obj.m_col_coords).p_Subtract(this.m_tf_offset);
					}
				}else{
					if(t_1==4){
						if(this.m_tf_box.p_Overlaps2(this.m_tf_line.m_o.p_Add(this.m_tf_line.m_d)) || this.m_tf_box.p_Overlaps2(this.m_tf_line.m_o)){
							t_res=1;
							t_coll_obj.m_col_coords.p_Overwrite3(this.m_tf_line.m_o.p_Add(this.m_tf_line.m_d));
							t_coll_obj.m_time=0.1;
						}else{
							var t_midvec=this.m_tf_line.m_o.p_Add(this.m_tf_line.m_d.p_Multiply5(0.5));
							if(this.m_tf_box.p_Overlaps2(t_midvec)){
								t_res=1;
								t_coll_obj.m_col_coords.p_Overwrite3(t_midvec);
								t_coll_obj.m_time=0.05;
							}
						}
						if((t_res)!=0){
							var t_norm=null;
							var t_cen=this.m_tf_box.p_Center();
							var t_nx=bb_math_Abs2(this.m_tf_line.m_d.m_x);
							var t_ny=bb_math_Abs2(this.m_tf_line.m_d.m_y);
							var t_nz=bb_math_Abs2(this.m_tf_line.m_d.m_z);
							if(t_nx>t_ny && t_nx>t_nz){
								t_norm=c_Vector.m_new.call(new c_Vector,-bb_math_Sgn2(this.m_tf_line.m_d.m_x),0.0,0.0);
							}else{
								if(t_ny>t_nx && t_ny>t_nz){
									t_norm=c_Vector.m_new.call(new c_Vector,0.0,-bb_math_Sgn2(this.m_tf_line.m_d.m_y),0.0);
								}else{
									if(t_nz>t_nx && t_nz>t_ny){
										t_norm=c_Vector.m_new.call(new c_Vector,0.0,0.0,-bb_math_Sgn2(this.m_tf_line.m_d.m_z));
									}else{
										t_norm=c_Vector.m_new.call(new c_Vector,-bb_math_Sgn2(this.m_tf_line.m_d.m_x),-bb_math_Sgn2(this.m_tf_line.m_d.m_y),-bb_math_Sgn2(this.m_tf_line.m_d.m_z));
									}
								}
							}
							t_coll_obj.m_normal=t_norm;
						}
					}
				}
			}
		}
	}
	return t_res;
}
c_CollisionInfo.prototype.p_DivSpheresCollisionDetect=function(t_ent,t_ent2,t_c_method,t_coll_obj){
	this.m_coll_method=t_c_method;
	var t_mesh=object_downcast((t_ent),c_TMesh);
	if(!((t_mesh)!=null)){
		return 0;
	}
	var t_hit=0;
	var t_total=this.m_src_divSphere.m_num;
	var t_skip=false;
	var t_line_o=this.m_coll_line.m_o.p_Copy();
	var t_line_d=this.m_dv.p_Copy();
	var t_temp_radius=this.m_src_radius;
	var t_finalNormal=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	var t_finalCoord=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	var t_finalRad=0.0;
	var t_finalDiv=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	var t_finalPos=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	var t_ctime=99999.0;
	for(var t_i=0;t_i<=t_total-1;t_i=t_i+1){
		var t_hh=0;
		this.m_src_radius=this.m_src_divSphere.m_rad[t_i]*1.001;
		this.m_src_divSphere.m_offset.p_Overwrite2(this.m_src_divSphere.m_pos[t_i].m_x,this.m_src_divSphere.m_pos[t_i].m_y,this.m_src_divSphere.m_pos[t_i].m_z);
		this.p_CollisionSetup(t_ent2,t_c_method,t_coll_obj,c_CollisionInfo.m_nullVec,t_skip);
		t_hh=this.p_CollisionDetect(t_coll_obj);
		if(((t_hh)!=0) && t_coll_obj.m_time<t_ctime){
			t_ctime=t_coll_obj.m_time;
			t_finalNormal=t_coll_obj.m_normal.p_Copy();
			t_finalCoord=t_coll_obj.m_col_coords.p_Copy();
			t_finalRad=this.m_src_divSphere.m_rad[t_i];
			t_finalDiv=this.m_src_divSphere.m_offset.p_Copy();
			t_finalPos=t_coll_obj.m_col_pos.p_Copy();
			t_hit=1;
		}
		t_skip=true;
	}
	if(t_hit>0){
		t_hit=1;
		t_coll_obj.m_normal=t_finalNormal;
		t_coll_obj.m_col_coords=t_finalCoord;
		this.m_hitDivRadius=t_finalRad;
		this.m_src_divSphere.m_offset=t_finalDiv;
		this.m_hitDivOffset.p_Overwrite3(t_finalDiv);
		t_coll_obj.m_time=t_ctime;
	}
	this.m_src_radius=t_temp_radius;
	return t_hit;
}
c_CollisionInfo.prototype.p_RegisterHitPlane=function(t_coords,t_normal){
	this.m_hits+=1;
	if(this.m_hits>4){
		return;
	}
	if(this.m_n_hit>4){
		return;
	}
	this.m_planes[this.m_n_hit]=c_Plane.m_new2.call(new c_Plane,t_coords,t_normal);
	this.m_col_points[this.m_n_hit]=t_coords.p_Copy();
	this.m_n_hit+=1;
}
c_CollisionInfo.prototype.p_CollisionResponse=function(t_col_obj,t_response,t_radius){
	if(this.m_hits>=10){
		return 0;
	}
	var t_new_offset=t_col_obj.m_normal.p_Multiply5(t_radius*1.001);
	var t_new_coords=t_col_obj.m_col_coords.p_Add(t_new_offset).p_Subtract(this.m_hitDivOffset);
	var t_coll_plane=c_Plane.m_new2.call(new c_Plane,t_new_coords,t_col_obj.m_normal);
	var t_adv=this.m_dv.p_Copy().p_Add(t_new_offset);
	if(t_response==1){
		this.m_dv.p_Overwrite3(this.m_sv);
		return 0;
	}
	this.m_sv.p_Overwrite3(t_new_coords);
	var t_nv=t_coll_plane.p_Nearest(t_adv);
	if(this.m_n_hit>0 && this.m_n_hit<4){
		var t_pn2=t_coll_plane.m_n.p_Add(this.m_planes[this.m_n_hit-1].m_n).p_Multiply5(0.5).p_Normalize();
		var t_c2=t_col_obj.m_col_coords.p_Add(this.m_col_points[this.m_n_hit-1]).p_Multiply5(0.5);
		var t_pl2=c_Plane.m_new2.call(new c_Plane,t_c2.p_Add(t_new_offset.p_Multiply5(1.001)).p_Subtract(this.m_hitDivOffset),t_pn2);
		t_nv=t_pl2.p_Nearest(t_adv);
		t_col_obj.m_normal=t_pn2;
		t_col_obj.m_col_coords=t_c2;
	}else{
		if(this.m_n_hit>3){
			t_nv=this.m_sv.p_Add(t_col_obj.m_normal.p_Multiply5(0.001));
			t_col_obj.m_col_coords=t_col_obj.m_col_coords.p_Add(t_col_obj.m_normal.p_Multiply5(0.001));
		}
	}
	var t_dd=t_nv.p_Subtract(t_adv);
	var t_d=t_dd.p_Dot(t_dd);
	if(t_d<0.00001){
		this.m_dv.p_Overwrite3(this.m_sv);
		return 0;
	}
	if(t_response==2){
	}else{
		if(t_response==3){
			t_nv.p_Overwrite2(t_nv.m_x,this.m_coll_line.m_o.m_y,t_nv.m_z);
			this.m_sv.m_y=this.m_coll_line.m_o.m_y;
		}
	}
	this.m_dv.p_Overwrite3(t_nv);
	this.m_coll_line.m_o.p_Overwrite3(this.m_sv);
	this.m_coll_line.m_d.p_Overwrite3(this.m_dv.p_Subtract(this.m_sv));
	return 1;
}
c_CollisionInfo.prototype.p_CollisionFinal=function(t_coll,t_response){
	if(t_response==0){
		return false;
	}
	if((this.m_hits)!=0){
		if(this.m_hits<10){
			this.m_dv.m_y*=this.m_inv_y_scale;
			t_coll.m_col_pos.p_Overwrite3(this.m_dv);
		}else{
			t_coll.m_col_pos.p_Overwrite3(this.m_panic);
		}
		return true;
	}
	return false;
}
c_CollisionInfo.m_UpdateCollisions=function(){
	var t_coll_obj=c_CollisionObject.m_new.call(new c_CollisionObject);
	c_CollisionInfo.m_col_info.p_Clear();
	for(var t_i=0;t_i<100;t_i=t_i+1){
		if(c_TCollisionPair.m_ent_lists[t_i]==null){
			continue;
		}
		var t_=c_TCollisionPair.m_ent_lists[t_i].p_ObjectEnumerator();
		while(t_.p_HasNext()){
			var t_ent=t_.p_NextObject();
			t_ent.m_collision.p_ClearImpact();
			if(t_ent.p_Hidden()==true){
				continue;
			}
			c_CollisionInfo.m_col_info.p_UpdateSourceSphere(t_ent);
			if(t_ent.m_collision.p_MoveTest(t_ent)==false){
				continue;
			}
			c_CollisionInfo.m_col_info.m_vec_a.p_Update(t_ent.m_mat.m_grid[3][0],t_ent.m_mat.m_grid[3][1],-t_ent.m_mat.m_grid[3][2]);
			c_CollisionInfo.m_col_info.m_vec_b.p_Update(t_ent.m_collision.m_old_px,t_ent.m_collision.m_old_py,t_ent.m_collision.m_old_pz);
			var t_sc=bb_math_Max2(bb_math_Max2(bb_math_Abs2(t_ent.m_gsx),bb_math_Abs2(t_ent.m_gsy)),bb_math_Abs2(t_ent.m_gsz));
			c_CollisionInfo.m_col_info.m_vec_c.p_Update(t_ent.m_collision.m_radius_x*t_sc,t_ent.m_collision.m_radius_y*t_sc,t_ent.m_collision.m_radius_x*t_sc);
			t_ent.m_collision.p_SetOldPosition(t_ent,t_ent.m_mat.m_grid[3][0],t_ent.m_mat.m_grid[3][1],-t_ent.m_mat.m_grid[3][2]);
			c_CollisionInfo.m_col_info.p_ClearHitPlanes();
			c_CollisionInfo.m_col_info.p_UpdateRay(c_CollisionInfo.m_col_info.m_vec_b,c_CollisionInfo.m_col_info.m_vec_a,c_CollisionInfo.m_col_info.m_vec_c);
			if((object_downcast((t_ent),c_TMesh))!=null){
				c_CollisionInfo.m_col_info.m_src_divSphere.p_RotateDivSpheres(t_ent,c_CollisionInfo.m_col_info,false);
			}
			var t_response=0;
			var t_obj_time=9999999.9;
			var t_pass2=false;
			var t_hit=0;
			c_CollisionInfo.m_col_info.m_col_passes=0;
			var t_original_src_radius=c_CollisionInfo.m_col_info.m_src_radius;
			var t_radvec=c_Vector.m_new.call(new c_Vector,c_CollisionInfo.m_col_info.m_src_radius,c_CollisionInfo.m_col_info.m_src_radius,c_CollisionInfo.m_col_info.m_src_radius);
			do{
				var t_ent2_hit=null;
				t_coll_obj.p_Clear();
				t_coll_obj.m_time=c_CollisionInfo.m_col_info.m_coll_line.m_o.p_DistanceSquared2(t_radvec)*1.1;
				c_CollisionInfo.m_starttime=t_coll_obj.m_time;
				if(t_ent.m_collision.m_radius_x==0.0){
					t_coll_obj.m_time=1.0;
				}
				var t_2=c_TCollisionPair.m_list.p_ObjectEnumerator();
				while(t_2.p_HasNext()){
					var t_col_pair=t_2.p_NextObject();
					if(t_col_pair.m_src_type==t_i){
						if(c_TCollisionPair.m_ent_lists[t_col_pair.m_des_type]==null){
							continue;
						}
						var t_3=c_TCollisionPair.m_ent_lists[t_col_pair.m_des_type].p_ObjectEnumerator();
						while(t_3.p_HasNext()){
							var t_ent2=t_3.p_NextObject();
							if(t_ent2.p_Hidden()==true){
								continue;
							}
							if(t_ent==t_ent2){
								continue;
							}
							c_CollisionInfo.m_col_info.p_UpdateDestShape(t_ent2);
							if(c_CollisionObject.m_QuickBoundsTest(c_CollisionInfo.m_col_info,t_ent2)){
								c_CollisionInfo.m_col_info.m_coll_method=t_col_pair.m_col_method;
								if(c_CollisionInfo.m_col_info.m_src_divSphere.m_num<=1){
									c_CollisionInfo.m_col_info.p_CollisionSetup(t_ent2,t_col_pair.m_col_method,t_coll_obj,c_CollisionInfo.m_nullVec,false);
									t_hit=c_CollisionInfo.m_col_info.p_CollisionDetect(t_coll_obj);
								}else{
									t_hit=c_CollisionInfo.m_col_info.p_DivSpheresCollisionDetect(t_ent,t_ent2,t_col_pair.m_col_method,t_coll_obj);
								}
								if((t_hit)!=0){
									c_CollisionInfo.m_col_info.p_RegisterHitPlane(t_coll_obj.m_col_coords,t_coll_obj.m_normal);
									t_ent2_hit=t_ent2;
									t_response=t_col_pair.m_response;
								}
							}
						}
					}
				}
				if(t_ent2_hit!=null){
					t_ent.m_collision.p_CreateImpact(t_ent2_hit,t_coll_obj);
					var t_res=1;
					if(c_CollisionInfo.m_col_info.m_src_divSphere.m_num>1){
						t_res=c_CollisionInfo.m_col_info.p_CollisionResponse(t_coll_obj,t_response,c_CollisionInfo.m_col_info.m_hitDivRadius);
					}else{
						t_res=c_CollisionInfo.m_col_info.p_CollisionResponse(t_coll_obj,t_response,c_CollisionInfo.m_col_info.m_src_radius);
					}
					if(t_res==0){
						break;
					}
				}else{
					break;
				}
				c_CollisionInfo.m_col_info.m_col_passes+=1;
			}while(!(false));
			var t_hits=((c_CollisionInfo.m_col_info.p_CollisionFinal(t_coll_obj,t_response))?1:0);
			if((t_hits)!=0){
				t_ent.p_PositionEntity(t_coll_obj.m_col_pos.m_x,t_coll_obj.m_col_pos.m_y,t_coll_obj.m_col_pos.m_z,1);
			}
			t_ent.m_collision.p_SetOldPosition(t_ent,t_ent.m_mat.m_grid[3][0],t_ent.m_mat.m_grid[3][1],-t_ent.m_mat.m_grid[3][2]);
		}
	}
	return 0;
}
function c_CollisionObject(){
	Object.call(this);
	this.m_time=99999999.0;
	this.m_normal=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_surface=0;
	this.m_index=0;
	this.m_obj_time=.0;
	this.m_col_coords=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_col_normal=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_col_time=0;
	this.m_col_surface=0;
	this.m_col_index=0;
	this.m_col_pos=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_col_box=c_Box.m_new.call(new c_Box);
	this.m_coll_u=.0;
	this.m_coll_v=.0;
}
c_CollisionObject.m_new=function(){
	return this;
}
c_CollisionObject.prototype.p_Clear=function(){
	this.m_time=99999999.0;
	this.m_normal=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_surface=0;
	this.m_index=0;
	this.m_obj_time=0.0;
	this.m_col_coords=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_col_normal=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_col_time=0;
	this.m_col_surface=0;
	this.m_col_index=0;
	this.m_col_pos=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_col_box=c_Box.m_new.call(new c_Box);
}
c_CollisionObject.m_QuickBoundsTest=function(t_col,t_ent2){
	var t_ds=t_col.m_ray_length*0.5+t_col.m_src_radius+t_col.m_src_radius+t_col.m_dst_radius;
	return t_col.m_dst_pos.p_DistanceSquared2(t_col.m_ray_center)<t_ds*t_ds;
}
c_CollisionObject.prototype.p_Update8=function(t_line,t_t,t_n,t_u,t_v,t_intersect){
	if(t_t>this.m_time){
		return 0;
	}
	var t_p=null;
	if((t_intersect)!=null){
		t_p=c_Plane.m_new2.call(new c_Plane,t_intersect,t_n);
	}else{
		t_p=c_Plane.m_new2.call(new c_Plane,t_line.p_Multiply5(t_t),t_n);
		if(t_p.m_n.p_Dot(t_line.m_o)+t_p.m_d<-0.00001){
			return 0;
		}
	}
	if(t_p.m_n.p_Dot(t_line.m_d)>-0.00001){
		return 0;
	}
	if((t_intersect)!=null){
		this.m_col_coords.p_Overwrite3(t_intersect);
	}
	this.m_time=t_t;
	this.m_normal.p_Overwrite3(t_n);
	this.m_coll_u=t_u;
	this.m_coll_v=t_v;
	return 1;
}
c_CollisionObject.prototype.p_SphereCollide=function(t_line,t_s_radius,t_dest,t_d_radius,t_offset2){
	var t_radius=t_s_radius+t_d_radius;
	var t_dd=t_dest.p_Subtract(t_line.m_o);
	var t_ld=t_line.m_d.p_Normalize();
	var t_b=t_ld.p_Dot(t_dd);
	if(t_b<0.0){
		return 0;
	}
	var t_c=t_dd.p_Dot(t_dd);
	var t_d=t_b*t_b-t_c+t_radius*t_radius;
	if(t_d<0.00001){
		return 0;
	}
	t_d=Math.sqrt(t_d);
	var t_f1=-t_b+t_d;
	var t_f2=-t_b-t_d;
	var t_f=t_f1;
	if(t_f2>t_f1){
		t_f=t_f2;
	}
	var t_i=t_ld.p_Multiply5(-t_f);
	var t_t=t_i.p_DistanceSquared();
	if(t_t>this.m_time){
		return 0;
	}
	var t_hh=0;
	if(t_t>t_line.m_d.p_DistanceSquared() && t_hh==0){
		return 0;
	}
	var t_nvec=t_i.p_Add(t_line.m_o).p_Subtract(t_dest).p_Normalize();
	return this.p_Update8(t_line,t_t,t_nvec,0.0,0.0,t_dest.p_Add(t_nvec.p_Multiply5(t_d_radius)));
}
c_CollisionObject.prototype.p_SphereTriangle=function(t_li,t_tf_radius,t_v0,t_v1,t_v2){
	var t_u=null;
	var t_v=null;
	var t_n=null;
	var t_dst=null;
	var t_w0=null;
	var t_w=null;
	var t_i=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	var t_ix=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	var t_r=-1.0;
	var t_a=.0;
	var t_b=0.0;
	var t_dotradius=.0;
	var t_ru=0.0;
	var t_rv=0.0;
	var t_beneath=false;
	t_u=t_v1.p_Subtract(t_v0);
	t_v=t_v2.p_Subtract(t_v0);
	t_n=t_u.p_Cross(t_v);
	if(t_n.m_x==0.0 && t_n.m_y==0.0 && t_n.m_z==0.0){
		return 0;
	}
	t_n=t_n.p_Normalize();
	var t_src=t_li.m_o.p_Subtract(t_v0);
	var t_n_radius=t_tf_radius.m_x*t_tf_radius.m_x;
	var t_o_len=t_n.p_Dot(t_src);
	if(t_o_len<0.0){
		return 0;
	}
	t_i=t_li.m_o.p_Subtract(t_n.p_Multiply5(t_o_len));
	var t_o_radius=t_i.p_DistanceSquared2(t_li.m_o);
	var t_diff=t_o_radius-t_n_radius;
	if(t_diff>this.m_time){
		return 0;
	}
	if(t_diff<0.0 && t_diff>-t_n_radius-0.000001){
		t_beneath=true;
		t_r=t_o_radius;
		t_ix=t_li.m_o;
	}else{
		var t_nr=t_tf_radius.m_x;
		t_a=t_o_len;
		t_b=-t_li.m_d.p_Dot(t_n);
		var t_b2=t_b+bb_math_Sgn2(t_b)*t_nr;
		var t_r2=0.0;
		if(t_b2==0.0){
			return 0;
		}
		t_r2=t_a/t_b2;
		if(t_r2<0.0 || t_r2>1.0){
			return 0;
		}
		t_ix=t_li.m_o.p_Add(t_li.m_d.p_Multiply5(t_r2));
		t_i=t_ix.p_Subtract(t_n.p_Multiply5(t_ix.p_Subtract(t_v0).p_Dot(t_n)));
		t_r=t_li.m_o.p_DistanceSquared2(t_ix);
	}
	if(t_r>this.m_time){
		return 0;
	}
	var t_uu=.0;
	var t_uv=.0;
	var t_vv=.0;
	var t_wu=.0;
	var t_wv=.0;
	var t_D=.0;
	t_uu=t_u.p_Dot(t_u);
	t_vv=t_v.p_Dot(t_v);
	t_w=t_i.p_Subtract(t_v0);
	if(t_w.m_x>0.0 && t_w.m_y>0.0 && t_w.m_z>0.0 && t_w.m_x>t_u.m_x && t_w.m_y>t_u.m_y && t_w.m_z>t_u.m_z && t_w.m_x>t_v.m_x && t_w.m_y>t_v.m_y && t_w.m_z>t_v.m_z){
		return 0;
	}
	if(t_w.m_x<0.0 && t_w.m_y<0.0 && t_w.m_z<0.0 && t_w.m_x<t_u.m_x && t_w.m_y<t_u.m_y && t_w.m_z<t_u.m_z && t_w.m_x<t_v.m_x && t_w.m_y<t_v.m_y && t_w.m_z<t_v.m_z){
		return 0;
	}
	t_uv=t_u.p_Dot(t_v);
	t_wu=t_w.p_Dot(t_u);
	t_wv=t_w.p_Dot(t_v);
	t_D=1.0/(t_uv*t_uv-t_uu*t_vv);
	var t_s=.0;
	var t_t=.0;
	var t_out=0;
	var t_bb=null;
	t_s=(t_uv*t_wv-t_vv*t_wu)*t_D;
	if(t_s<0.0 || t_s>1.0){
		t_out=1;
	}
	t_t=(t_uv*t_wu-t_uu*t_wv)*t_D;
	if(t_t<0.0 || t_s+t_t>1.0){
		t_out=t_out+1;
	}
	if(t_out>0 && ((t_tf_radius.m_x)!=0.0)){
		if(t_s+t_t>1.0){
			t_bb=t_ix.p_PointOnSegment(t_v1,t_v2);
		}else{
			if(t_t<0.0){
				t_bb=t_ix.p_PointOnSegment(t_v0,t_v1);
			}else{
				t_bb=t_ix.p_PointOnSegment(t_v0,t_v2);
			}
		}
		var t_nr2=t_ix.p_DistanceSquared2(t_bb)*1.001;
		var t_offset=0.0;
		if(t_nr2>t_n_radius+t_offset || t_nr2>this.m_time){
			return 0;
		}
		t_r=t_nr2-t_offset;
		var t_ld=t_li.m_d.p_Multiply5(0.1);
		t_n=t_ix.p_Subtract(t_bb).p_Normalize();
		t_bb=t_bb.p_Subtract(t_n.p_Multiply5(0.01));
		t_i=t_bb;
	}else{
		if(t_out>0){
			return 0;
		}
	}
	return this.p_Update8(t_li,t_r,t_n,t_s,t_t,t_i);
}
c_CollisionObject.prototype.p_RayTriangle=function(t_li,t_v0,t_v1,t_v2){
	var t_u=null;
	var t_v=null;
	var t_n=null;
	var t_w=null;
	var t_i=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	var t_r=.0;
	var t_a=.0;
	var t_b=.0;
	var t_radius=.0;
	t_u=t_v1.p_Subtract(t_v0);
	t_v=t_v2.p_Subtract(t_v0);
	t_n=t_u.p_Cross(t_v);
	if(t_n.m_x==0.0 && t_n.m_y==0.0 && t_n.m_z==0.0){
		return 0;
	}
	t_a=t_li.m_o.p_Subtract(t_v0).p_Dot(t_n);
	t_b=-t_li.m_d.p_Dot(t_n);
	if(t_b==0.0){
		if(t_a==0.0){
			t_r=0.0;
		}else{
			return 0;
		}
	}else{
		t_r=t_a/t_b;
		t_i.p_Overwrite2(t_li.m_o.m_x+t_li.m_d.m_x*t_r,t_li.m_o.m_y+t_li.m_d.m_y*t_r,t_li.m_o.m_z+t_li.m_d.m_z*t_r);
	}
	if(t_r>this.m_time){
		return 0;
	}
	var t_uu=.0;
	var t_uv=.0;
	var t_vv=.0;
	var t_wu=.0;
	var t_wv=.0;
	var t_D=.0;
	t_uu=t_u.p_Dot(t_u);
	t_vv=t_v.p_Dot(t_v);
	t_w=t_i.p_Subtract(t_v0);
	var t_ids=t_w.p_DistanceSquared();
	if(t_ids>t_uu && t_ids>t_vv){
		return 0;
	}
	t_uv=t_u.p_Dot(t_v);
	t_wu=t_w.p_Dot(t_u);
	t_wv=t_w.p_Dot(t_v);
	t_D=1.0/(t_uv*t_uv-t_uu*t_vv);
	var t_s=.0;
	var t_t=.0;
	var t_out=0;
	var t_diff=0.0;
	t_s=(t_uv*t_wv-t_vv*t_wu)*t_D;
	if(t_s<0.0 || t_s>1.0){
		return 0;
	}
	t_t=(t_uv*t_wu-t_uu*t_wv)*t_D;
	if(t_t<0.0 || t_s+t_t>1.0){
		return 0;
	}
	return this.p_Update8(t_li,t_r,t_n,t_s,t_t,t_i.p_Copy());
}
c_CollisionObject.m_BOXQUADS=[];
c_CollisionObject.prototype.p_SphereBox=function(t_li,t_s_radvec,t_box,t_scale){
	var t_hit=0;
	for(var t_n=0;t_n<=23;t_n=t_n+4){
		var t_v0=t_box.p_Corner(c_CollisionObject.m_BOXQUADS[t_n]);
		var t_v1=t_box.p_Corner(c_CollisionObject.m_BOXQUADS[t_n+1]);
		var t_v2=t_box.p_Corner(c_CollisionObject.m_BOXQUADS[t_n+2]);
		var t_v3=t_box.p_Corner(c_CollisionObject.m_BOXQUADS[t_n+3]);
		t_hit=t_hit|this.p_SphereTriangle(t_li,t_s_radvec,t_v0,t_v1,t_v2)|this.p_SphereTriangle(t_li,t_s_radvec,t_v0,t_v2,t_v3);
	}
	if((t_hit)!=0){
		this.m_col_box=t_box.p_Copy();
	}
	return t_hit;
}
function c_TransformMat(){
	Object.call(this);
	this.m_m=null;
	this.m_v=null;
}
c_TransformMat.m_new=function(){
	this.m_m=c_Matrix.m_new.call(new c_Matrix);
	this.m_v=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	return this;
}
c_TransformMat.m_new2=function(t_mm,t_vv){
	this.m_v=t_vv.p_Copy();
	this.m_m=t_mm.p_Copy();
	return this;
}
c_TransformMat.prototype.p_Multiply2=function(t_q){
	return this.m_m.p_Multiply2(t_q).p_Add(this.m_v);
}
c_TransformMat.prototype.p_Multiply3=function(t_q){
	var t_t=this.m_m.p_Multiply2(t_q.m_o).p_Add(this.m_v);
	return c_Line.m_new2.call(new c_Line,t_t,this.m_m.p_Multiply2(t_q.m_o.p_Add(t_q.m_d)).p_Add(this.m_v).p_Subtract(t_t));
}
c_TransformMat.prototype.p_Multiply7=function(t_q){
	var t_b=c_Box.m_new2.call(new c_Box,this.m_m.p_Multiply2(t_q.p_Corner(0)).p_Add(this.m_v));
	for(var t_k=1;t_k<=7;t_k=t_k+1){
		t_b.p_Update10(this.m_m.p_Multiply2(t_q.p_Corner(t_k)).p_Add(this.m_v));
	}
	return t_b;
}
c_TransformMat.prototype.p_Multiply8=function(t_q){
	var t_t=c_TransformMat.m_new.call(new c_TransformMat);
	t_t.m_m=this.m_m.p_Copy();
	var t_temp_m=this.m_m.p_Copy();
	t_t.m_m.p_Multiply(t_q.m_m);
	t_t.m_v=t_temp_m.p_Multiply2(t_q.m_v).p_Add(this.m_v);
	return t_t;
}
function c_Plane(){
	Object.call(this);
	this.m_n=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_d=0.0;
}
c_Plane.m_new=function(t_nn,t_dd){
	this.m_n=t_nn.p_Copy();
	this.m_d=t_dd;
	return this;
}
c_Plane.m_new2=function(t_p,t_nn){
	this.m_n=t_nn.p_Copy();
	this.m_d=-this.m_n.p_Dot(t_p);
	return this;
}
c_Plane.m_new3=function(t_v0,t_v1,t_v2){
	this.m_n=t_v1.p_Subtract(t_v0).p_Cross(t_v2.p_Subtract(t_v0)).p_Normalize();
	this.m_d=-this.m_n.p_Dot(t_v0);
	return this;
}
c_Plane.m_new4=function(){
	return this;
}
c_Plane.prototype.p_Update9=function(t_nnx,t_nny,t_nnz,t_dd){
	this.m_n.m_x=t_nnx;
	this.m_n.m_y=t_nny;
	this.m_n.m_z=t_nnz;
	this.m_d=t_dd;
	return 0;
}
c_Plane.prototype.p_Distance=function(t_q){
	return this.m_n.p_Dot(t_q)+this.m_d;
}
c_Plane.prototype.p_Nearest=function(t_q){
	return t_q.p_Subtract(this.m_n.p_Multiply5(this.p_Distance(t_q)));
}
function c_DivSphereInfo(){
	Object.call(this);
	this.m_offset=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	this.m_pos=[];
	this.m_rad=[];
	this.m_num=0;
}
c_DivSphereInfo.m_new=function(){
	return this;
}
c_DivSphereInfo.prototype.p_Clear=function(){
	this.m_offset.p_Overwrite2(0.0,0.0,0.0);
	this.m_pos=new_object_array(0);
	this.m_rad=new_number_array(0);
	this.m_num=0;
}
c_DivSphereInfo.prototype.p_RotateDivSpheres=function(t_ent,t_col_info,t_inverse){
	this.m_num=0;
	this.m_offset.p_Overwrite2(0.0,0.0,0.0);
	var t_mesh=object_downcast((t_ent),c_TMesh);
	if(((t_mesh)!=null) && t_mesh.m_col_tree.m_divSphere_p.length>1){
		var t_quickmat=t_ent.m_mat.p_Copy();
		t_quickmat.m_grid[3][0]=0.0;
		t_quickmat.m_grid[3][1]=0.0;
		t_quickmat.m_grid[3][2]=0.0;
		if(t_inverse){
			t_quickmat=t_quickmat.p_Inverse();
		}
		if(!((this.m_pos).length!=0) || this.m_pos.length<t_mesh.m_col_tree.m_divSphere_p.length){
			this.m_pos=new_object_array(t_mesh.m_col_tree.m_divSphere_p.length);
			this.m_rad=new_number_array(t_mesh.m_col_tree.m_divSphere_p.length);
		}
		var t_radmax=bb_math_Max2(bb_math_Max2(t_ent.m_gsx,t_ent.m_gsy),t_ent.m_gsz);
		for(var t_i=0;t_i<=t_mesh.m_col_tree.m_divSphere_p.length-1;t_i=t_i+1){
			this.m_pos[t_i]=t_quickmat.p_Multiply2(t_mesh.m_col_tree.m_divSphere_p[t_i]);
			this.m_rad[t_i]=t_mesh.m_col_tree.m_divSphere_r[t_i]*t_radmax;
		}
		this.m_num=t_mesh.m_col_tree.m_divSphere_p.length;
		return this.m_num;
	}
	return 0;
}
function c_TCollisionPair(){
	Object.call(this);
	this.m_src_type=0;
	this.m_des_type=0;
	this.m_col_method=0;
	this.m_response=0;
}
c_TCollisionPair.m_ent_lists=[];
c_TCollisionPair.m_list=null;
function c_TCollisionImpact(){
	Object.call(this);
	this.m_x=.0;
	this.m_y=.0;
	this.m_z=.0;
	this.m_nx=.0;
	this.m_ny=.0;
	this.m_nz=.0;
	this.m_ent=null;
	this.m_surf=0;
	this.m_tri=0;
}
c_TCollisionImpact.m_new=function(){
	return this;
}
function bb_math_Abs(t_x){
	if(t_x>=0){
		return t_x;
	}
	return -t_x;
}
function bb_math_Abs2(t_x){
	if(t_x>=0.0){
		return t_x;
	}
	return -t_x;
}
function c_Enumerator6(){
	Object.call(this);
	this.m__list=null;
	this.m__curr=null;
}
c_Enumerator6.m_new=function(t_list){
	this.m__list=t_list;
	this.m__curr=t_list.m__head.m__succ;
	return this;
}
c_Enumerator6.m_new2=function(){
	return this;
}
c_Enumerator6.prototype.p_HasNext=function(){
	while(this.m__curr.m__succ.m__pred!=this.m__curr){
		this.m__curr=this.m__curr.m__succ;
	}
	return this.m__curr!=this.m__list.m__head;
}
c_Enumerator6.prototype.p_NextObject=function(){
	var t_data=this.m__curr.m__data;
	this.m__curr=this.m__curr.m__succ;
	return t_data;
}
function c_Box(){
	Object.call(this);
	this.m_a=null;
	this.m_b=null;
}
c_Box.m_new=function(){
	this.m_a=c_Vector.m_new.call(new c_Vector,999999999.000,999999999.000,999999999.000);
	this.m_b=c_Vector.m_new.call(new c_Vector,-999999999.000,-999999999.000,-999999999.000);
	return this;
}
c_Box.m_new2=function(t_q){
	this.m_a=t_q.p_Copy();
	this.m_b=t_q.p_Copy();
	return this;
}
c_Box.m_new3=function(t_aa,t_bb){
	this.m_a=t_aa.p_Copy();
	this.m_b=t_bb.p_Copy();
	return this;
}
c_Box.m_new4=function(t_aa,t_bb,t_cc){
	this.m_a=t_aa.p_Copy();
	this.m_b=t_aa.p_Copy();
	var t_q=t_bb;
	if(t_q.m_x<this.m_a.m_x){
		this.m_a.m_x=t_q.m_x;
	}
	if(t_q.m_y<this.m_a.m_y){
		this.m_a.m_y=t_q.m_y;
	}
	if(t_q.m_z<this.m_a.m_z){
		this.m_a.m_z=t_q.m_z;
	}
	if(t_q.m_x>this.m_b.m_x){
		this.m_b.m_x=t_q.m_x;
	}
	if(t_q.m_y>this.m_b.m_y){
		this.m_b.m_y=t_q.m_y;
	}
	if(t_q.m_z>this.m_b.m_z){
		this.m_b.m_z=t_q.m_z;
	}
	t_q=t_cc;
	if(t_q.m_x<this.m_a.m_x){
		this.m_a.m_x=t_q.m_x;
	}
	if(t_q.m_y<this.m_a.m_y){
		this.m_a.m_y=t_q.m_y;
	}
	if(t_q.m_z<this.m_a.m_z){
		this.m_a.m_z=t_q.m_z;
	}
	if(t_q.m_x>this.m_b.m_x){
		this.m_b.m_x=t_q.m_x;
	}
	if(t_q.m_y>this.m_b.m_y){
		this.m_b.m_y=t_q.m_y;
	}
	if(t_q.m_z>this.m_b.m_z){
		this.m_b.m_z=t_q.m_z;
	}
	return this;
}
c_Box.prototype.p_Update10=function(t_q){
	if(t_q.m_x<this.m_a.m_x){
		this.m_a.m_x=t_q.m_x;
	}
	if(t_q.m_y<this.m_a.m_y){
		this.m_a.m_y=t_q.m_y;
	}
	if(t_q.m_z<this.m_a.m_z){
		this.m_a.m_z=t_q.m_z;
	}
	if(t_q.m_x>this.m_b.m_x){
		this.m_b.m_x=t_q.m_x;
	}
	if(t_q.m_y>this.m_b.m_y){
		this.m_b.m_y=t_q.m_y;
	}
	if(t_q.m_z>this.m_b.m_z){
		this.m_b.m_z=t_q.m_z;
	}
	return 0;
}
c_Box.prototype.p_Update11=function(t_q){
	if(t_q.m_a.m_x<this.m_a.m_x){
		this.m_a.m_x=t_q.m_a.m_x;
	}
	if(t_q.m_a.m_y<this.m_a.m_y){
		this.m_a.m_y=t_q.m_a.m_y;
	}
	if(t_q.m_a.m_z<this.m_a.m_z){
		this.m_a.m_z=t_q.m_a.m_z;
	}
	if(t_q.m_b.m_x>this.m_b.m_x){
		this.m_b.m_x=t_q.m_b.m_x;
	}
	if(t_q.m_b.m_y>this.m_b.m_y){
		this.m_b.m_y=t_q.m_b.m_y;
	}
	if(t_q.m_b.m_z>this.m_b.m_z){
		this.m_b.m_z=t_q.m_b.m_z;
	}
	return 0;
}
c_Box.prototype.p_Update4=function(t_l){
	this.m_a.p_Overwrite3(t_l.m_o);
	this.m_b.p_Overwrite3(t_l.m_o);
	this.p_Update10(t_l.m_o.p_Add(t_l.m_d));
	return 0;
}
c_Box.m_new5=function(t_l){
	this.m_a=t_l.m_o.p_Copy();
	this.m_b=t_l.m_o.p_Copy();
	this.p_Update10(t_l.m_o.p_Add(t_l.m_d));
	return this;
}
c_Box.prototype.p_Corner=function(t_n){
	var t_q=null;
	var t_w=null;
	var t_e=null;
	if((t_n&1)!=0){
		t_q=this.m_b;
	}else{
		t_q=this.m_a;
	}
	if((t_n&2)!=0){
		t_w=this.m_b;
	}else{
		t_w=this.m_a;
	}
	if((t_n&4)!=0){
		t_e=this.m_b;
	}else{
		t_e=this.m_a;
	}
	return c_Vector.m_new.call(new c_Vector,t_q.m_x,t_w.m_y,t_e.m_z);
}
c_Box.prototype.p_Clear=function(){
	this.m_a.m_x=999999999.000;
	this.m_a.m_y=999999999.000;
	this.m_a.m_z=999999999.000;
	this.m_b.m_x=-999999999.000;
	this.m_b.m_y=-999999999.000;
	this.m_b.m_z=-999999999.000;
	return 0;
}
c_Box.prototype.p_Scale2=function(t_n){
	this.m_a.m_x*=t_n.m_x;
	this.m_a.m_y*=t_n.m_y;
	this.m_a.m_z*=t_n.m_z;
	this.m_b.m_x*=t_n.m_x;
	this.m_b.m_y*=t_n.m_y;
	this.m_b.m_z*=t_n.m_z;
	return 0;
}
c_Box.prototype.p_Width=function(){
	return this.m_b.m_x-this.m_a.m_x;
}
c_Box.prototype.p_Height=function(){
	return this.m_b.m_y-this.m_a.m_y;
}
c_Box.prototype.p_Depth=function(){
	return this.m_b.m_z-this.m_a.m_z;
}
c_Box.prototype.p_Overlaps=function(t_q){
	return (this.m_b.m_x<t_q.m_b.m_x && this.m_b.m_x>=this.m_a.m_x && this.m_b.m_x>=t_q.m_a.m_x || this.m_b.m_x>t_q.m_b.m_x && t_q.m_b.m_x>=this.m_a.m_x && t_q.m_b.m_x>=t_q.m_a.m_x) && (this.m_b.m_y<t_q.m_b.m_y && this.m_b.m_y>=this.m_a.m_y && this.m_b.m_y>=t_q.m_a.m_y || this.m_b.m_y>t_q.m_b.m_y && t_q.m_b.m_y>=this.m_a.m_y && t_q.m_b.m_y>=t_q.m_a.m_y) && (this.m_b.m_z<t_q.m_b.m_z && this.m_b.m_z>=this.m_a.m_z && this.m_b.m_z>=t_q.m_a.m_z || this.m_b.m_z>t_q.m_b.m_z && t_q.m_b.m_z>=this.m_a.m_z && t_q.m_b.m_z>=t_q.m_a.m_z);
}
c_Box.prototype.p_Overlaps2=function(t_q){
	return t_q.m_x<this.m_b.m_x && t_q.m_x>=this.m_a.m_x && (t_q.m_y<this.m_b.m_y && t_q.m_y>=this.m_a.m_y) && (t_q.m_z<this.m_b.m_z && t_q.m_z>=this.m_a.m_z);
}
c_Box.prototype.p_Center=function(){
	return c_Vector.m_new.call(new c_Vector,(this.m_a.m_x+this.m_b.m_x)*0.5,(this.m_a.m_y+this.m_b.m_y)*0.5,(this.m_a.m_z+this.m_b.m_z)*0.5);
}
c_Box.prototype.p_Copy=function(){
	return c_Box.m_new3.call(new c_Box,this.m_a,this.m_b);
}
function c_List10(){
	Object.call(this);
	this.m__head=(c_HeadNode10.m_new.call(new c_HeadNode10));
}
c_List10.m_new=function(){
	return this;
}
c_List10.prototype.p_AddLast10=function(t_data){
	return c_Node12.m_new.call(new c_Node12,this.m__head,this.m__head.m__pred,t_data);
}
c_List10.m_new2=function(t_data){
	var t_=t_data;
	var t_2=0;
	while(t_2<t_.length){
		var t_t=t_[t_2];
		t_2=t_2+1;
		this.p_AddLast10(t_t);
	}
	return this;
}
c_List10.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator7.m_new.call(new c_Enumerator7,this);
}
function c_Node12(){
	Object.call(this);
	this.m__succ=null;
	this.m__pred=null;
	this.m__data=null;
}
c_Node12.m_new=function(t_succ,t_pred,t_data){
	this.m__succ=t_succ;
	this.m__pred=t_pred;
	this.m__succ.m__pred=this;
	this.m__pred.m__succ=this;
	this.m__data=t_data;
	return this;
}
c_Node12.m_new2=function(){
	return this;
}
function c_HeadNode10(){
	c_Node12.call(this);
}
c_HeadNode10.prototype=extend_class(c_Node12);
c_HeadNode10.m_new=function(){
	c_Node12.m_new2.call(this);
	this.m__succ=(this);
	this.m__pred=(this);
	return this;
}
function c_Enumerator7(){
	Object.call(this);
	this.m__list=null;
	this.m__curr=null;
}
c_Enumerator7.m_new=function(t_list){
	this.m__list=t_list;
	this.m__curr=t_list.m__head.m__succ;
	return this;
}
c_Enumerator7.m_new2=function(){
	return this;
}
c_Enumerator7.prototype.p_HasNext=function(){
	while(this.m__curr.m__succ.m__pred!=this.m__curr){
		this.m__curr=this.m__curr.m__succ;
	}
	return this.m__curr!=this.m__list.m__head;
}
c_Enumerator7.prototype.p_NextObject=function(){
	var t_data=this.m__curr.m__data;
	this.m__curr=this.m__curr.m__succ;
	return t_data;
}
function c_Vec3(){
	c_Vector.call(this);
}
c_Vec3.prototype=extend_class(c_Vector);
c_Vec3.m_Add=function(t_a,t_b,t_out){
	t_out.m_x=t_a.m_x+t_b.m_x;
	t_out.m_y=t_a.m_y+t_b.m_y;
	t_out.m_z=t_a.m_z+t_b.m_z;
}
c_Vec3.m_Multiply=function(t_a,t_b,t_out){
	t_out.m_x=t_a.m_x*t_b.m_x;
	t_out.m_y=t_a.m_y*t_b.m_y;
	t_out.m_z=t_a.m_z*t_b.m_z;
}
c_Vec3.m_Multiply2=function(t_a,t_b,t_out){
	t_out.m_x=t_a.m_x*t_b;
	t_out.m_y=t_a.m_y*t_b;
	t_out.m_z=t_a.m_z*t_b;
}
c_Vec3.m_temp=null;
c_Vec3.m_Multiply3=function(t_a,t_b,t_out){
	c_Vec3.m_temp.m_x=t_b.m_grid[0][0]*t_a.m_x+t_b.m_grid[1][0]*t_a.m_y+t_b.m_grid[2][0]*t_a.m_z;
	c_Vec3.m_temp.m_y=t_b.m_grid[0][1]*t_a.m_x+t_b.m_grid[1][1]*t_a.m_y+t_b.m_grid[2][1]*t_a.m_z;
	c_Vec3.m_temp.m_z=t_b.m_grid[0][2]*t_a.m_x+t_b.m_grid[1][2]*t_a.m_y+t_b.m_grid[2][2]*t_a.m_z;
	t_out.m_x=c_Vec3.m_temp.m_x;
	t_out.m_y=c_Vec3.m_temp.m_y;
	t_out.m_z=c_Vec3.m_temp.m_z;
}
function c_MeshCollider(){
	Object.call(this);
	this.m_tri_count=0;
	this.m_vert_count=0;
	this.m_tris=[];
	this.m_tri_surface=[];
	this.m_tri_number=[];
	this.m_tri_vix=[];
	this.m_tri_verts=[];
	this.m_tri_centres=[];
	this.m_leaf_list=c_List11.m_new.call(new c_List11);
	this.m_tree=null;
	this.m_r_invx=.0;
	this.m_r_invy=.0;
	this.m_r_invz=.0;
	this.m_r_sign=new_number_array(3);
	this.m_tri_node_stack=c_Stack4.m_new.call(new c_Stack4);
}
c_MeshCollider.m_new=function(t_no_verts,t_no_tris){
	this.m_tri_count=t_no_tris;
	this.m_vert_count=t_no_verts;
	this.m_tris=new_number_array(t_no_tris);
	this.m_tri_surface=new_number_array(t_no_tris);
	this.m_tri_number=new_number_array(t_no_tris);
	this.m_tri_vix=new_number_array(t_no_tris*3);
	this.m_tri_verts=new_object_array(t_no_verts);
	this.m_tri_centres=new_object_array(t_no_tris);
	for(var t_i=0;t_i<=t_no_verts-1;t_i=t_i+1){
		this.m_tri_verts[t_i]=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	}
	for(var t_i2=0;t_i2<=t_no_tris-1;t_i2=t_i2+1){
		this.m_tri_centres[t_i2]=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	}
	return this;
}
c_MeshCollider.m_new2=function(){
	return this;
}
c_MeshCollider.prototype.p_CreateNodeBox=function(t_tris){
	if(t_tris.length<1){
		return c_Box.m_new.call(new c_Box);
	}
	var t_box=c_Box.m_new.call(new c_Box);
	for(var t_k=0;t_k<=t_tris.length-1;t_k=t_k+1){
		if(t_tris[t_k]!=0){
			var t_ti=t_tris[t_k]*3;
			t_box.p_Update10(this.m_tri_verts[this.m_tri_vix[t_ti+0]]);
			t_box.p_Update10(this.m_tri_verts[this.m_tri_vix[t_ti+1]]);
			t_box.p_Update10(this.m_tri_verts[this.m_tri_vix[t_ti+2]]);
		}
	}
	return t_box;
}
c_MeshCollider.prototype.p_CreateLeaf=function(t_tris){
	var t_c=c_Node13.m_new.call(new c_Node13);
	t_c.m_box=this.p_CreateNodeBox(t_tris);
	t_c.m_triangles=t_tris;
	this.m_leaf_list.p_AddLast11(t_c);
	t_c.m_level=9999;
	return t_c;
}
c_MeshCollider.prototype.p_CreateNode=function(t_tris,t_level,t_max_tris){
	if(t_tris.length<=t_max_tris){
		return this.p_CreateLeaf(t_tris);
	}
	var t_c=c_Node13.m_new.call(new c_Node13);
	t_c.m_box=this.p_CreateNodeBox(t_tris);
	t_c.m_level=t_level;
	var t_max=t_c.m_box.p_Width();
	var t_axis=0;
	if(t_c.m_box.p_Height()>t_max){
		t_max=t_c.m_box.p_Height();
		t_axis=1;
	}
	if(t_c.m_box.p_Depth()>t_max){
		t_max=t_c.m_box.p_Depth();
		t_axis=2;
	}
	var t_k=0;
	var t_tri=0;
	var t_axis_map=c_PairList.m_new.call(new c_PairList);
	var t_num=t_tris.length;
	var t_real_total=0;
	for(t_k=0;t_k<=t_num-1;t_k=t_k+1){
		t_tri=t_tris[t_k];
		if(t_tri==0){
			continue;
		}
		var t_ap=c_AxisPair.m_new.call(new c_AxisPair);
		if(t_axis==0){
			t_ap.m_key=this.m_tri_centres[t_tri].m_x;
			t_ap.m_value=t_tri;
		}else{
			if(t_axis==1){
				t_ap.m_key=this.m_tri_centres[t_tri].m_y;
				t_ap.m_value=t_tri;
			}else{
				t_ap.m_key=this.m_tri_centres[t_tri].m_z;
				t_ap.m_value=t_tri;
			}
		}
		t_axis_map.p_AddLast12(t_ap);
		t_real_total+=1;
	}
	t_axis_map.p_Sort(1);
	var t_index=0;
	var t_num_left=(((t_real_total)*0.5)|0);
	var t_num_right=t_real_total-t_num_left;
	var t_newtris=new_number_array(t_num_left+1);
	var t_newtris2=new_number_array(t_num_right+1);
	var t_leftset=1;
	var t_lastval=-1;
	var t_=t_axis_map.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_ap2=t_.p_NextObject();
		if(t_index<=t_num_left){
			t_newtris[t_index]=t_ap2.m_value;
			t_lastval=t_ap2.m_value;
		}else{
			t_newtris2[t_index-t_num_left-1]=t_ap2.m_value;
			t_lastval=t_ap2.m_value;
		}
		t_index+=1;
	}
	t_c.m_left=this.p_CreateNode(t_newtris,t_level+1,t_max_tris);
	t_c.m_right=this.p_CreateNode(t_newtris2,t_level+1,t_max_tris);
	return t_c;
}
c_MeshCollider.m_ray_line=null;
c_MeshCollider.prototype.p_RayBoxSetup=function(t_line2){
	c_MeshCollider.m_ray_line=t_line2;
	var t_ln=1.0/t_line2.p_Length();
	var t_dir=c_Vector.m_new.call(new c_Vector,(t_line2.m_o.m_x-t_line2.m_d.m_x)*t_ln,(t_line2.m_o.m_y-t_line2.m_d.m_y)*t_ln,(t_line2.m_o.m_z-t_line2.m_d.m_z)*t_ln);
	if(t_ln!=1.0){
		this.m_r_invx=1.0/t_dir.m_x;
		this.m_r_invy=1.0/t_dir.m_y;
		this.m_r_invz=1.0/t_dir.m_z;
	}
	this.m_r_sign[0]=((this.m_r_invx<0.0)?1:0);
	this.m_r_sign[1]=((this.m_r_invy<0.0)?1:0);
	this.m_r_sign[2]=((this.m_r_invz<0.0)?1:0);
	return 0;
}
c_MeshCollider.prototype.p_ClearTriNodeStack=function(){
	this.m_tri_node_stack.p_Clear();
}
c_MeshCollider.prototype.p_RayBoxTest=function(t_li,t_box){
	var t_tmin=.0;
	var t_tmax=.0;
	var t_tymin=.0;
	var t_tymax=.0;
	var t_tzmin=.0;
	var t_tzmax=.0;
	var t_bounds=new_object_array(2);
	t_bounds[0]=t_box.m_a;
	t_bounds[1]=t_box.m_b;
	t_tmin=(t_bounds[this.m_r_sign[0]].m_x-t_li.m_o.m_x)*this.m_r_invx;
	t_tmax=(t_bounds[1-this.m_r_sign[0]].m_x-t_li.m_o.m_x)*this.m_r_invx;
	t_tymin=(t_bounds[this.m_r_sign[1]].m_y-t_li.m_o.m_y)*this.m_r_invy;
	t_tymax=(t_bounds[1-this.m_r_sign[1]].m_y-t_li.m_o.m_y)*this.m_r_invy;
	if(t_tmin>t_tymax || t_tymin>t_tmax){
		return 0;
	}
	if(t_tymin>t_tmin){
		t_tmin=t_tymin;
	}
	if(t_tymax<t_tmax){
		t_tmax=t_tymax;
	}
	t_tzmin=(t_bounds[this.m_r_sign[2]].m_z-t_li.m_o.m_z)*this.m_r_invz;
	t_tzmax=(t_bounds[1-this.m_r_sign[2]].m_z-t_li.m_o.m_z)*this.m_r_invz;
	if(t_tmin>t_tzmax || t_tzmin>t_tmax){
		return 0;
	}
	return 1;
}
c_MeshCollider.prototype.p_CollideNodeAABB=function(t_line_box,t_radius,t_curr_coll,t_node,t_check_only){
	if(t_node==null){
		return 0;
	}
	if(t_node==this.m_tree && t_check_only==false){
		this.p_ClearTriNodeStack();
	}
	if(!t_line_box.p_Overlaps(t_node.m_box)){
		return 0;
	}
	if(t_radius.m_x==0.0 && !((this.p_RayBoxTest(c_MeshCollider.m_ray_line,t_node.m_box))!=0)){
		return 0;
	}
	var t_hit=0;
	if(t_node.m_triangles.length<1){
		if((t_node.m_left)!=null){
			t_hit=t_hit|this.p_CollideNodeAABB(t_line_box,t_radius,t_curr_coll,t_node.m_left,false);
		}
		if((t_node.m_right)!=null){
			t_hit=t_hit|this.p_CollideNodeAABB(t_line_box,t_radius,t_curr_coll,t_node.m_right,false);
		}
	}else{
		t_hit=1;
		if(t_check_only==false){
			this.m_tri_node_stack.p_Push10(t_node);
		}
	}
	return t_hit;
}
c_MeshCollider.prototype.p_TriNodeCollide=function(t_line_box,t_line,t_s_radius,t_coll_obj,t_scalef,t_bypass_test){
	var t_hit=0;
	var t_tritest=0;
	var t_str="";
	var t_v0=null;
	var t_v1=null;
	var t_v2=null;
	var t_tri_box=c_Box.m_new.call(new c_Box);
	var t_=this.m_tri_node_stack.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_node=t_.p_NextObject();
		for(var t_k=0;t_k<=t_node.m_triangles.length-1;t_k=t_k+1){
			var t_miss=0;
			var t_tri=t_node.m_triangles[t_k]*3;
			t_v0=this.m_tri_verts[this.m_tri_vix[t_tri+0]];
			t_v1=this.m_tri_verts[this.m_tri_vix[t_tri+1]];
			t_v2=this.m_tri_verts[this.m_tri_vix[t_tri+2]];
			t_tri_box.m_a.p_Overwrite3(t_v0);
			t_tri_box.m_b.p_Overwrite3(t_v0);
			t_tri_box.p_Update10(t_v1);
			t_tri_box.p_Update10(t_v2);
			if(t_tri_box.p_Overlaps(t_line_box)){
				if(t_bypass_test){
					var t_norm=null;
					var t_cen=t_tri_box.p_Center();
					var t_nx=bb_math_Abs2(t_line.m_d.m_x);
					var t_ny=bb_math_Abs2(t_line.m_d.m_y);
					var t_nz=bb_math_Abs2(t_line.m_d.m_z);
					if(t_nx>t_ny && t_nx>t_nz){
						t_norm=c_Vector.m_new.call(new c_Vector,-bb_math_Sgn2(t_line.m_d.m_x),0.0,0.0);
					}else{
						if(t_ny>t_nx && t_ny>t_nz){
							t_norm=c_Vector.m_new.call(new c_Vector,0.0,-bb_math_Sgn2(t_line.m_d.m_y),0.0);
						}else{
							if(t_nz>t_nx && t_nz>t_ny){
								t_norm=c_Vector.m_new.call(new c_Vector,0.0,0.0,-bb_math_Sgn2(t_line.m_d.m_z));
							}else{
								t_norm=c_Vector.m_new.call(new c_Vector,-bb_math_Sgn2(t_line.m_d.m_x),-bb_math_Sgn2(t_line.m_d.m_y),-bb_math_Sgn2(t_line.m_d.m_z));
							}
						}
					}
					t_coll_obj.m_surface=this.m_tri_surface[t_node.m_triangles[t_k]];
					t_coll_obj.m_index=this.m_tri_number[t_node.m_triangles[t_k]];
					t_coll_obj.m_normal=t_norm;
					t_coll_obj.m_col_coords=t_line.m_o.p_Add(t_line.m_d);
					t_coll_obj.m_time=0.5;
					return 1;
				}
				if(t_s_radius.m_x>0.0001){
					if(!((t_coll_obj.p_SphereTriangle(t_line,t_s_radius,t_v0.p_Multiply2(t_scalef),t_v1.p_Multiply2(t_scalef),t_v2.p_Multiply2(t_scalef)))!=0)){
						t_miss=1;
					}
				}else{
					if(!((t_coll_obj.p_RayTriangle(t_line,t_v0.p_Multiply2(t_scalef),t_v1.p_Multiply2(t_scalef),t_v2.p_Multiply2(t_scalef)))!=0)){
						t_miss=1;
					}
				}
				if(!((t_miss)!=0)){
					t_coll_obj.m_surface=this.m_tri_surface[t_node.m_triangles[t_k]];
					t_coll_obj.m_index=this.m_tri_number[t_node.m_triangles[t_k]];
					t_hit+=1;
				}
			}
		}
	}
	return t_hit;
}
function c_Node13(){
	Object.call(this);
	this.m_box=null;
	this.m_triangles=[];
	this.m_level=0;
	this.m_left=null;
	this.m_right=null;
	this.m_name="";
}
c_Node13.m_new=function(){
	return this;
}
function c_List11(){
	Object.call(this);
	this.m__head=(c_HeadNode11.m_new.call(new c_HeadNode11));
}
c_List11.m_new=function(){
	return this;
}
c_List11.prototype.p_AddLast11=function(t_data){
	return c_Node14.m_new.call(new c_Node14,this.m__head,this.m__head.m__pred,t_data);
}
c_List11.m_new2=function(t_data){
	var t_=t_data;
	var t_2=0;
	while(t_2<t_.length){
		var t_t=t_[t_2];
		t_2=t_2+1;
		this.p_AddLast11(t_t);
	}
	return this;
}
function c_Node14(){
	Object.call(this);
	this.m__succ=null;
	this.m__pred=null;
	this.m__data=null;
}
c_Node14.m_new=function(t_succ,t_pred,t_data){
	this.m__succ=t_succ;
	this.m__pred=t_pred;
	this.m__succ.m__pred=this;
	this.m__pred.m__succ=this;
	this.m__data=t_data;
	return this;
}
c_Node14.m_new2=function(){
	return this;
}
function c_HeadNode11(){
	c_Node14.call(this);
}
c_HeadNode11.prototype=extend_class(c_Node14);
c_HeadNode11.m_new=function(){
	c_Node14.m_new2.call(this);
	this.m__succ=(this);
	this.m__pred=(this);
	return this;
}
function c_AxisPair(){
	Object.call(this);
	this.m_key=.0;
	this.m_value=0;
}
c_AxisPair.m_new=function(){
	return this;
}
function c_List12(){
	Object.call(this);
	this.m__head=(c_HeadNode12.m_new.call(new c_HeadNode12));
}
c_List12.m_new=function(){
	return this;
}
c_List12.prototype.p_AddLast12=function(t_data){
	return c_Node15.m_new.call(new c_Node15,this.m__head,this.m__head.m__pred,t_data);
}
c_List12.m_new2=function(t_data){
	var t_=t_data;
	var t_2=0;
	while(t_2<t_.length){
		var t_t=t_[t_2];
		t_2=t_2+1;
		this.p_AddLast12(t_t);
	}
	return this;
}
c_List12.prototype.p_Compare4=function(t_lhs,t_rhs){
	error("Unable to compare items");
	return 0;
}
c_List12.prototype.p_Sort=function(t_ascending){
	var t_ccsgn=-1;
	if((t_ascending)!=0){
		t_ccsgn=1;
	}
	var t_insize=1;
	do{
		var t_merges=0;
		var t_tail=this.m__head;
		var t_p=this.m__head.m__succ;
		while(t_p!=this.m__head){
			t_merges+=1;
			var t_q=t_p.m__succ;
			var t_qsize=t_insize;
			var t_psize=1;
			while(t_psize<t_insize && t_q!=this.m__head){
				t_psize+=1;
				t_q=t_q.m__succ;
			}
			do{
				var t_t=null;
				if(((t_psize)!=0) && ((t_qsize)!=0) && t_q!=this.m__head){
					var t_cc=this.p_Compare4(t_p.m__data,t_q.m__data)*t_ccsgn;
					if(t_cc<=0){
						t_t=t_p;
						t_p=t_p.m__succ;
						t_psize-=1;
					}else{
						t_t=t_q;
						t_q=t_q.m__succ;
						t_qsize-=1;
					}
				}else{
					if((t_psize)!=0){
						t_t=t_p;
						t_p=t_p.m__succ;
						t_psize-=1;
					}else{
						if(((t_qsize)!=0) && t_q!=this.m__head){
							t_t=t_q;
							t_q=t_q.m__succ;
							t_qsize-=1;
						}else{
							break;
						}
					}
				}
				t_t.m__pred=t_tail;
				t_tail.m__succ=t_t;
				t_tail=t_t;
			}while(!(false));
			t_p=t_q;
		}
		t_tail.m__succ=this.m__head;
		this.m__head.m__pred=t_tail;
		if(t_merges<=1){
			return 0;
		}
		t_insize*=2;
	}while(!(false));
}
c_List12.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator8.m_new.call(new c_Enumerator8,this);
}
function c_PairList(){
	c_List12.call(this);
}
c_PairList.prototype=extend_class(c_List12);
c_PairList.m_new=function(){
	c_List12.m_new.call(this);
	return this;
}
c_PairList.prototype.p_Compare4=function(t_lh,t_rh){
	if(t_lh.m_key<t_rh.m_key){
		return -1;
	}
	return ((t_lh.m_key>t_rh.m_key)?1:0);
}
function c_Node15(){
	Object.call(this);
	this.m__succ=null;
	this.m__pred=null;
	this.m__data=null;
}
c_Node15.m_new=function(t_succ,t_pred,t_data){
	this.m__succ=t_succ;
	this.m__pred=t_pred;
	this.m__succ.m__pred=this;
	this.m__pred.m__succ=this;
	this.m__data=t_data;
	return this;
}
c_Node15.m_new2=function(){
	return this;
}
function c_HeadNode12(){
	c_Node15.call(this);
}
c_HeadNode12.prototype=extend_class(c_Node15);
c_HeadNode12.m_new=function(){
	c_Node15.m_new2.call(this);
	this.m__succ=(this);
	this.m__pred=(this);
	return this;
}
function c_Enumerator8(){
	Object.call(this);
	this.m__list=null;
	this.m__curr=null;
}
c_Enumerator8.m_new=function(t_list){
	this.m__list=t_list;
	this.m__curr=t_list.m__head.m__succ;
	return this;
}
c_Enumerator8.m_new2=function(){
	return this;
}
c_Enumerator8.prototype.p_HasNext=function(){
	while(this.m__curr.m__succ.m__pred!=this.m__curr){
		this.m__curr=this.m__curr.m__succ;
	}
	return this.m__curr!=this.m__list.m__head;
}
c_Enumerator8.prototype.p_NextObject=function(){
	var t_data=this.m__curr.m__data;
	this.m__curr=this.m__curr.m__succ;
	return t_data;
}
function c_Stack4(){
	Object.call(this);
	this.m_data=[];
	this.m_length=0;
}
c_Stack4.m_new=function(){
	return this;
}
c_Stack4.m_new2=function(t_data){
	this.m_data=t_data.slice(0);
	this.m_length=t_data.length;
	return this;
}
c_Stack4.m_NIL=null;
c_Stack4.prototype.p_Clear=function(){
	for(var t_i=0;t_i<this.m_length;t_i=t_i+1){
		this.m_data[t_i]=c_Stack4.m_NIL;
	}
	this.m_length=0;
}
c_Stack4.prototype.p_Push10=function(t_value){
	if(this.m_length==this.m_data.length){
		this.m_data=resize_object_array(this.m_data,this.m_length*2+10);
	}
	this.m_data[this.m_length]=t_value;
	this.m_length+=1;
}
c_Stack4.prototype.p_Push11=function(t_values,t_offset,t_count){
	for(var t_i=0;t_i<t_count;t_i=t_i+1){
		this.p_Push10(t_values[t_offset+t_i]);
	}
}
c_Stack4.prototype.p_Push12=function(t_values,t_offset){
	this.p_Push11(t_values,t_offset,t_values.length-t_offset);
}
c_Stack4.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator9.m_new.call(new c_Enumerator9,this);
}
c_Stack4.prototype.p_Length2=function(t_newlength){
	if(t_newlength<this.m_length){
		for(var t_i=t_newlength;t_i<this.m_length;t_i=t_i+1){
			this.m_data[t_i]=c_Stack4.m_NIL;
		}
	}else{
		if(t_newlength>this.m_data.length){
			this.m_data=resize_object_array(this.m_data,bb_math_Max(this.m_length*2+10,t_newlength));
		}
	}
	this.m_length=t_newlength;
}
c_Stack4.prototype.p_Length=function(){
	return this.m_length;
}
function c_Enumerator9(){
	Object.call(this);
	this.m_stack=null;
	this.m_index=0;
}
c_Enumerator9.m_new=function(t_stack){
	this.m_stack=t_stack;
	return this;
}
c_Enumerator9.m_new2=function(){
	return this;
}
c_Enumerator9.prototype.p_HasNext=function(){
	return this.m_index<this.m_stack.p_Length();
}
c_Enumerator9.prototype.p_NextObject=function(){
	this.m_index+=1;
	return this.m_stack.m_data[this.m_index-1];
}
function bb_math_Sgn(t_x){
	if(t_x<0){
		return -1;
	}
	return ((t_x>0)?1:0);
}
function bb_math_Sgn2(t_x){
	if(t_x<0.0){
		return -1.0;
	}
	if(t_x>0.0){
		return 1.0;
	}
	return 0.0;
}
function c_TAnimation(){
	Object.call(this);
}
c_TAnimation.m_VertexDeform=function(t_ent){
	var t_ov=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	var t_x=0.0;
	var t_y=0.0;
	var t_z=0.0;
	var t_bone=null;
	var t_weight=.0;
	var t_=t_ent.m_surf_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_surf=t_.p_NextObject();
		var t_anim_surf=t_ent.p_GetAnimSurface(t_surf);
		if(!((t_anim_surf)!=null)){
			continue;
		}
		t_anim_surf.m_reset_vbo=t_anim_surf.m_reset_vbo|1;
		t_anim_surf.m_vbo_dyn=true;
		var t_vid=0;
		var t_vid3=0;
		for(var t_vid2=0;t_vid2<=t_anim_surf.m_no_verts-1;t_vid2=t_vid2+1){
			t_vid3=t_vid2*3;
			var t_tweight=0.0;
			if(t_anim_surf.m_vert_bone1_no[t_vid2]!=0){
				t_bone=t_ent.m_bones[t_anim_surf.m_vert_bone1_no[t_vid2]-1];
				t_weight=t_anim_surf.m_vert_weight1[t_vid2];
				t_tweight+=t_weight;
				if(t_weight>0.0){
					t_surf.m_vert_data.p_GetVertCoords(t_ov,t_vid2);
					t_x=(t_bone.m_tform_mat.m_grid[0][0]*t_ov.m_x+t_bone.m_tform_mat.m_grid[1][0]*t_ov.m_y+t_bone.m_tform_mat.m_grid[2][0]*t_ov.m_z+t_bone.m_tform_mat.m_grid[3][0])*t_weight;
					t_y=(t_bone.m_tform_mat.m_grid[0][1]*t_ov.m_x+t_bone.m_tform_mat.m_grid[1][1]*t_ov.m_y+t_bone.m_tform_mat.m_grid[2][1]*t_ov.m_z+t_bone.m_tform_mat.m_grid[3][1])*t_weight;
					t_z=(t_bone.m_tform_mat.m_grid[0][2]*t_ov.m_x+t_bone.m_tform_mat.m_grid[1][2]*t_ov.m_y+t_bone.m_tform_mat.m_grid[2][2]*t_ov.m_z+t_bone.m_tform_mat.m_grid[3][2])*t_weight;
				}
				if(t_anim_surf.m_vert_bone2_no[t_vid2]!=0 && t_anim_surf.m_vert_weight2[t_vid2]>0.0){
					t_bone=t_ent.m_bones[t_anim_surf.m_vert_bone2_no[t_vid2]-1];
					t_weight=t_anim_surf.m_vert_weight2[t_vid2];
					t_tweight+=t_weight;
					t_x+=(t_bone.m_tform_mat.m_grid[0][0]*t_ov.m_x+t_bone.m_tform_mat.m_grid[1][0]*t_ov.m_y+t_bone.m_tform_mat.m_grid[2][0]*t_ov.m_z+t_bone.m_tform_mat.m_grid[3][0])*t_weight;
					t_y+=(t_bone.m_tform_mat.m_grid[0][1]*t_ov.m_x+t_bone.m_tform_mat.m_grid[1][1]*t_ov.m_y+t_bone.m_tform_mat.m_grid[2][1]*t_ov.m_z+t_bone.m_tform_mat.m_grid[3][1])*t_weight;
					t_z+=(t_bone.m_tform_mat.m_grid[0][2]*t_ov.m_x+t_bone.m_tform_mat.m_grid[1][2]*t_ov.m_y+t_bone.m_tform_mat.m_grid[2][2]*t_ov.m_z+t_bone.m_tform_mat.m_grid[3][2])*t_weight;
					if(t_anim_surf.m_vert_bone3_no[t_vid2]!=0 && t_anim_surf.m_vert_weight3[t_vid2]>0.0){
						t_bone=t_ent.m_bones[t_anim_surf.m_vert_bone3_no[t_vid2]-1];
						t_weight=t_anim_surf.m_vert_weight3[t_vid2];
						t_tweight+=t_weight;
						t_x+=(t_bone.m_tform_mat.m_grid[0][0]*t_ov.m_x+t_bone.m_tform_mat.m_grid[1][0]*t_ov.m_y+t_bone.m_tform_mat.m_grid[2][0]*t_ov.m_z+t_bone.m_tform_mat.m_grid[3][0])*t_weight;
						t_y+=(t_bone.m_tform_mat.m_grid[0][1]*t_ov.m_x+t_bone.m_tform_mat.m_grid[1][1]*t_ov.m_y+t_bone.m_tform_mat.m_grid[2][1]*t_ov.m_z+t_bone.m_tform_mat.m_grid[3][1])*t_weight;
						t_z+=(t_bone.m_tform_mat.m_grid[0][2]*t_ov.m_x+t_bone.m_tform_mat.m_grid[1][2]*t_ov.m_y+t_bone.m_tform_mat.m_grid[2][2]*t_ov.m_z+t_bone.m_tform_mat.m_grid[3][2])*t_weight;
						if(t_anim_surf.m_vert_bone4_no[t_vid2]!=0 && t_anim_surf.m_vert_weight4[t_vid2]>0.0){
							t_bone=t_ent.m_bones[t_anim_surf.m_vert_bone4_no[t_vid2]-1];
							t_weight=t_anim_surf.m_vert_weight4[t_vid2];
							t_tweight+=t_weight;
							t_x+=(t_bone.m_tform_mat.m_grid[0][0]*t_ov.m_x+t_bone.m_tform_mat.m_grid[1][0]*t_ov.m_y+t_bone.m_tform_mat.m_grid[2][0]*t_ov.m_z+t_bone.m_tform_mat.m_grid[3][0])*t_weight;
							t_y+=(t_bone.m_tform_mat.m_grid[0][1]*t_ov.m_x+t_bone.m_tform_mat.m_grid[1][1]*t_ov.m_y+t_bone.m_tform_mat.m_grid[2][1]*t_ov.m_z+t_bone.m_tform_mat.m_grid[3][1])*t_weight;
							t_z+=(t_bone.m_tform_mat.m_grid[0][2]*t_ov.m_x+t_bone.m_tform_mat.m_grid[1][2]*t_ov.m_y+t_bone.m_tform_mat.m_grid[2][2]*t_ov.m_z+t_bone.m_tform_mat.m_grid[3][2])*t_weight;
						}
					}
				}
				t_anim_surf.m_vert_data.p_PokeVertCoords(t_vid2,t_x,t_y,t_z);
			}
		}
	}
}
c_TAnimation.m_AnimateMesh2=function(t_ent1,t_framef,t_start_frame,t_end_frame){
	var t_temp_vec=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	var t_mesh=object_downcast((t_ent1),c_TMesh);
	if(t_mesh!=null){
		if(t_mesh.m_anim==0 || t_mesh.p_Hidden()==true){
			return 0;
		}
		t_mesh.m_anim_render=1;
		var t_quat=c_Quaternion.m_new.call(new c_Quaternion);
		var t_=t_mesh.m_bones;
		var t_2=0;
		while(t_2<t_.length){
			var t_bent=t_[t_2];
			t_2=t_2+1;
			var t_i=0;
			var t_ii=0;
			var t_fd1=t_framef;
			var t_fd2=1.0-t_fd1;
			var t_found=0;
			var t_no_keys=0;
			var t_w1=.0;
			var t_x1=t_bent.m_kx;
			var t_y1=t_bent.m_ky;
			var t_z1=t_bent.m_kz;
			var t_w2=.0;
			var t_x2=.0;
			var t_y2=.0;
			var t_z2=.0;
			var t_flag=0;
			t_i=t_start_frame-1;
			do{
				t_i=t_i+1;
				if(t_i>t_end_frame){
					t_i=t_start_frame;
					t_ii=t_ii+1;
				}
				t_flag=t_bent.m_keys.m_flags[t_i]&1;
				if((t_flag)!=0){
					t_x2=t_bent.m_keys.m_px[t_i];
					t_y2=t_bent.m_keys.m_py[t_i];
					t_z2=t_bent.m_keys.m_pz[t_i];
					t_found=1;
				}
			}while(!(t_found==1 || t_ii>=2));
			if(t_found==0){
				t_no_keys=1;
			}
			t_found=0;
			t_ii=0;
			var t_px3=0.0;
			var t_py3=0.0;
			var t_pz3=0.0;
			if(t_no_keys==1){
				t_px3=t_bent.m_n_px;
				t_py3=t_bent.m_n_py;
				t_pz3=t_bent.m_n_pz;
			}else{
				if(t_fd1+t_fd2==0.0){
					t_px3=t_x1;
					t_py3=t_y1;
					t_pz3=t_z1;
				}else{
					var t_fd_inv=1.0/(t_fd1+t_fd2);
					t_px3=(t_x2-t_x1)*t_fd_inv*t_fd1+t_x1;
					t_py3=(t_y2-t_y1)*t_fd_inv*t_fd1+t_y1;
					t_pz3=(t_z2-t_z1)*t_fd_inv*t_fd1+t_z1;
				}
			}
			t_no_keys=0;
			t_w1=t_bent.m_kqw;
			t_x1=t_bent.m_kqx;
			t_y1=t_bent.m_kqy;
			t_z1=t_bent.m_kqz;
			t_i=t_start_frame-1;
			do{
				t_i=t_i+1;
				if(t_i>t_end_frame){
					t_i=t_start_frame;
					t_ii=t_ii+1;
				}
				t_flag=t_bent.m_keys.m_flags[t_i]&4;
				if((t_flag)!=0){
					t_w2=t_bent.m_keys.m_qw[t_i];
					t_x2=t_bent.m_keys.m_qx[t_i];
					t_y2=t_bent.m_keys.m_qy[t_i];
					t_z2=t_bent.m_keys.m_qz[t_i];
					t_found=1;
				}
			}while(!(t_found==1 || t_ii>=2));
			if(t_found==0){
				t_no_keys=1;
			}
			t_found=0;
			t_ii=0;
			var t_w3=0.0;
			var t_x3=0.0;
			var t_y3=0.0;
			var t_z3=0.0;
			if(t_no_keys==1){
				t_w3=t_bent.m_n_qw;
				t_x3=t_bent.m_n_qx;
				t_y3=t_bent.m_n_qy;
				t_z3=t_bent.m_n_qz;
			}else{
				if(t_fd1+t_fd2==0.0){
					t_w3=t_w1;
					t_x3=t_x1;
					t_y3=t_y1;
					t_z3=t_z1;
				}else{
					var t_t=1.0/(t_fd1+t_fd2)*t_fd1;
					t_quat=c_Quaternion.m_Slerp(t_x1,t_y1,t_z1,t_w1,t_x2,t_y2,t_z2,t_w2,t_t);
				}
			}
			t_no_keys=0;
			t_temp_vec.p_Update(t_px3,t_py3,t_pz3);
			t_bent.p_Transform(t_temp_vec,t_quat,true);
		}
		c_TAnimation.m_VertexDeform(t_mesh);
	}
	return 0;
}
c_TAnimation.m_AnimateVertex=function(t_ent,t_frame,t_start_frame,t_end_frame){
	var t_vx=.0;
	var t_vy=.0;
	var t_vz=.0;
	var t_has_animation=false;
	var t_mesh=object_downcast((t_ent),c_TMesh);
	if(!((t_ent)!=null)){
		return 0;
	}
	if(t_frame>(t_end_frame)){
		t_frame=(t_end_frame);
	}
	if(t_frame<(t_start_frame)){
		t_frame=(t_start_frame);
	}
	var t_anim_surf=null;
	var t_=t_mesh.m_surf_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_surf=t_.p_NextObject();
		t_anim_surf=t_mesh.p_GetAnimSurface(t_surf);
		if(!((t_anim_surf)!=null)){
			continue;
		}
		t_has_animation=true;
		t_anim_surf.m_vbo_dyn=true;
		if((t_anim_surf.m_vert_anim[((t_frame)|0)])!=null){
			t_mesh.m_anim_surf_frame[t_surf.m_surf_id]=((t_frame)|0);
			t_anim_surf.m_reset_vbo=t_anim_surf.m_reset_vbo|1;
		}
	}
	if(t_has_animation){
		t_mesh.m_anim_render=1;
	}
	return 0;
}
c_TAnimation.m_AnimateMesh=function(t_ent1,t_framef,t_start_frame,t_end_frame){
	var t_temp_vec=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	var t_quat=c_Quaternion.m_new.call(new c_Quaternion);
	var t_mesh=object_downcast((t_ent1),c_TMesh);
	if(t_mesh!=null){
		if(t_mesh.m_anim==0 || t_mesh.p_Hidden()==true){
			return 0;
		}
		t_mesh.m_anim_render=1;
		if(t_framef>(t_end_frame)){
			t_framef=(t_end_frame);
		}
		if(t_framef<(t_start_frame)){
			t_framef=(t_start_frame);
		}
		var t_=t_mesh.m_bones;
		var t_2=0;
		while(t_2<t_.length){
			var t_bent=t_[t_2];
			t_2=t_2+1;
			if(!((t_bent)!=null)){
				continue;
			}
			t_temp_vec=t_bent.m_keys.p_GetPosition(t_framef,t_start_frame,t_end_frame,true);
			t_bent.m_kx=t_temp_vec.m_x;
			t_bent.m_ky=t_temp_vec.m_y;
			t_bent.m_kz=t_temp_vec.m_z;
			t_quat=t_bent.m_keys.p_GetQuaternion(t_framef,t_start_frame,t_end_frame,true);
			t_bent.m_kqw=t_quat.m_w;
			t_bent.m_kqx=t_quat.m_x;
			t_bent.m_kqy=t_quat.m_y;
			t_bent.m_kqz=t_quat.m_z;
			t_bent.p_Transform(t_temp_vec,t_quat,false);
		}
		c_TBone.m_UpdateBoneChildren(t_mesh);
		c_TAnimation.m_VertexDeform(t_mesh);
	}
	return 0;
}
function c_Quaternion(){
	Object.call(this);
	this.m_x=.0;
	this.m_y=.0;
	this.m_z=.0;
	this.m_w=.0;
}
c_Quaternion.m_new=function(){
	return this;
}
c_Quaternion.m_new2=function(t_xx,t_yy,t_zz,t_ww){
	this.m_x=t_xx;
	this.m_y=t_yy;
	this.m_z=t_zz;
	this.m_w=t_ww;
	return this;
}
c_Quaternion.m_Slerp=function(t_ax,t_ay,t_az,t_aw,t_bx,t_by,t_bz,t_bw,t_t){
	var t_var=c_Quaternion.m_new.call(new c_Quaternion);
	if(bb_math_Abs2(t_ax-t_bx)<0.001 && bb_math_Abs2(t_ay-t_by)<0.001 && bb_math_Abs2(t_az-t_bz)<0.001 && bb_math_Abs2(t_aw-t_bw)<0.001){
		t_var.m_x=t_bx;
		t_var.m_y=t_by;
		t_var.m_z=t_bz;
		t_var.m_w=t_bw;
		return t_var;
	}
	var t_cosineom=t_ax*t_bx+t_ay*t_by+t_az*t_bz+t_aw*t_bw;
	var t_scaler_w=.0;
	var t_scaler_x=.0;
	var t_scaler_y=.0;
	var t_scaler_z=.0;
	if(t_cosineom<=0.0){
		t_cosineom=-t_cosineom;
		t_scaler_w=-t_bw;
		t_scaler_x=-t_bx;
		t_scaler_y=-t_by;
		t_scaler_z=-t_bz;
	}else{
		t_scaler_w=t_bw;
		t_scaler_x=t_bx;
		t_scaler_y=t_by;
		t_scaler_z=t_bz;
	}
	var t_scale0=.0;
	var t_scale1=.0;
	if(1.0-t_cosineom>0.0001){
		var t_omega=(Math.acos(t_cosineom)*R2D);
		var t_sineom=1.0/Math.sin((t_omega)*D2R);
		t_scale0=Math.sin(((1.0-t_t)*t_omega)*D2R)*t_sineom;
		t_scale1=Math.sin((t_t*t_omega)*D2R)*t_sineom;
	}else{
		t_scale0=1.0-t_t;
		t_scale1=t_t;
	}
	t_var.m_w=t_scale0*t_aw+t_scale1*t_scaler_w;
	t_var.m_x=t_scale0*t_ax+t_scale1*t_scaler_x;
	t_var.m_y=t_scale0*t_ay+t_scale1*t_scaler_y;
	t_var.m_z=t_scale0*t_az+t_scale1*t_scaler_z;
	return t_var;
}
c_Quaternion.m_QuatToMatrix=function(t_x,t_y,t_z,t_w,t_mat){
	var t_x2=.0;
	var t_xx2=.0;
	var t_xy2=.0;
	var t_xz2=.0;
	var t_xw2=.0;
	var t_y2=.0;
	var t_yy2=.0;
	var t_yz2=.0;
	var t_yw2=.0;
	var t_z2=.0;
	var t_zz2=.0;
	var t_zw2=.0;
	t_x2=t_x+t_x;
	t_xx2=t_x2*t_x;
	t_xy2=t_x2*t_y;
	t_xz2=t_x2*t_z;
	t_xw2=t_x2*t_w;
	t_y2=t_y+t_y;
	t_yy2=t_y2*t_y;
	t_yz2=t_y2*t_z;
	t_yw2=t_y2*t_w;
	t_z2=t_z+t_z;
	t_zz2=t_z2*t_z;
	t_zw2=t_z2*t_w;
	t_mat.m_grid[0][0]=1.0-t_yy2-t_zz2;
	t_mat.m_grid[0][1]=t_xy2-t_zw2;
	t_mat.m_grid[0][2]=t_xz2+t_yw2;
	t_mat.m_grid[1][0]=t_xy2+t_zw2;
	t_mat.m_grid[1][1]=1.0-t_xx2-t_zz2;
	t_mat.m_grid[1][2]=t_yz2-t_xw2;
	t_mat.m_grid[2][0]=t_xz2-t_yw2;
	t_mat.m_grid[2][1]=t_yz2+t_xw2;
	t_mat.m_grid[2][2]=1.0-t_xx2-t_yy2;
	t_mat.m_grid[3][0]=0.0;
	t_mat.m_grid[3][1]=0.0;
	t_mat.m_grid[3][2]=0.0;
	t_mat.m_grid[0][3]=0.0;
	t_mat.m_grid[1][3]=0.0;
	t_mat.m_grid[2][3]=0.0;
	t_mat.m_grid[3][3]=1.0;
}
function c_TAnimationKeys(){
	Object.call(this);
	this.m_flags=new_number_array(1);
	this.m_px=new_number_array(1);
	this.m_py=new_number_array(1);
	this.m_pz=new_number_array(1);
	this.m_qw=new_number_array(1);
	this.m_qx=new_number_array(1);
	this.m_qy=new_number_array(1);
	this.m_qz=new_number_array(1);
}
c_TAnimationKeys.prototype.p_GetPosition=function(t_frame,t_f_start,t_f_end,t_loop){
	var t_i=0;
	var t_ii=0;
	var t_fd1=0.0;
	var t_fd2=0.0;
	var t_found=false;
	var t_no_keys=false;
	var t_w1=.0;
	var t_x1=.0;
	var t_y1=.0;
	var t_z1=.0;
	var t_w2=.0;
	var t_x2=.0;
	var t_y2=.0;
	var t_z2=.0;
	var t_flag=0;
	t_i=((t_frame)|0)+1;
	do{
		t_i=t_i-1;
		t_flag=this.m_flags[t_i]&1;
		if((t_flag)!=0){
			t_x1=this.m_px[t_i];
			t_y1=this.m_py[t_i];
			t_z1=this.m_pz[t_i];
			t_fd1=t_frame-(t_i);
			t_found=true;
		}
		if(t_i<=t_f_start){
			t_i=t_f_end+1;
			t_ii=t_ii+1;
		}
	}while(!(t_found==true || t_ii>=2));
	if(t_found==false){
		t_no_keys=true;
	}
	t_found=false;
	t_ii=0;
	t_i=((t_frame)|0);
	do{
		t_i=t_i+1;
		if(t_i>t_f_end){
			t_i=t_f_start;
			t_ii=t_ii+1;
		}
		t_flag=this.m_flags[t_i]&1;
		if((t_flag)!=0){
			t_x2=this.m_px[t_i];
			t_y2=this.m_py[t_i];
			t_z2=this.m_pz[t_i];
			t_fd2=(t_i)-t_frame;
			t_found=true;
		}
	}while(!(t_found==true || t_ii>=2));
	if(t_found==false){
		t_no_keys=true;
	}
	t_found=false;
	t_ii=0;
	var t_px3=0.0;
	var t_py3=0.0;
	var t_pz3=0.0;
	if(t_no_keys==true){
		t_px3=0.0;
		t_py3=0.0;
		t_pz3=0.0;
	}else{
		if(t_fd1+t_fd2==0.0){
			t_px3=t_x1;
			t_py3=t_y1;
			t_pz3=t_z1;
		}else{
			var t_fd_inv=1.0/(t_fd1+t_fd2);
			t_px3=(t_x2-t_x1)*t_fd_inv*t_fd1+t_x1;
			t_py3=(t_y2-t_y1)*t_fd_inv*t_fd1+t_y1;
			t_pz3=(t_z2-t_z1)*t_fd_inv*t_fd1+t_z1;
		}
	}
	t_no_keys=false;
	return c_Vector.m_new.call(new c_Vector,t_px3,t_py3,t_pz3);
}
c_TAnimationKeys.prototype.p_GetQuaternion=function(t_frame,t_f_start,t_f_end,t_loop){
	var t_i=0;
	var t_ii=0;
	var t_fd1=0.0;
	var t_fd2=0.0;
	var t_found=false;
	var t_no_keys=false;
	var t_w1=.0;
	var t_x1=.0;
	var t_y1=.0;
	var t_z1=.0;
	var t_w2=.0;
	var t_x2=.0;
	var t_y2=.0;
	var t_z2=.0;
	var t_flag=0;
	var t_quat=c_Quaternion.m_new.call(new c_Quaternion);
	t_i=((t_frame)|0)+1;
	do{
		t_i=t_i-1;
		t_flag=this.m_flags[t_i]&4;
		if((t_flag)!=0){
			t_w1=this.m_qw[t_i];
			t_x1=this.m_qx[t_i];
			t_y1=this.m_qy[t_i];
			t_z1=this.m_qz[t_i];
			t_fd1=t_frame-(t_i);
			t_found=true;
		}
		if(t_i<=t_f_start){
			t_i=t_f_end+1;
			t_ii=t_ii+1;
		}
	}while(!(t_found==true || t_ii>=2));
	if(t_found==false){
		t_no_keys=true;
	}
	t_found=false;
	t_ii=0;
	t_i=((t_frame)|0);
	do{
		t_i=t_i+1;
		if(t_i>t_f_end){
			t_i=t_f_start;
			t_ii=t_ii+1;
		}
		t_flag=this.m_flags[t_i]&4;
		if((t_flag)!=0){
			t_w2=this.m_qw[t_i];
			t_x2=this.m_qx[t_i];
			t_y2=this.m_qy[t_i];
			t_z2=this.m_qz[t_i];
			t_fd2=(t_i)-t_frame;
			t_found=true;
		}
	}while(!(t_found==true || t_ii>=2));
	if(t_found==false){
		t_no_keys=true;
	}
	t_found=false;
	t_ii=0;
	var t_w3=0.0;
	var t_x3=0.0;
	var t_y3=0.0;
	var t_z3=0.0;
	if(t_no_keys==true){
		t_quat.m_w=1.0;
		t_quat.m_x=0.0;
		t_quat.m_y=0.0;
		t_quat.m_z=0.0;
	}else{
		if(t_fd1+t_fd2==0.0){
			t_quat.m_w=t_w1;
			t_quat.m_x=t_x1;
			t_quat.m_y=t_y1;
			t_quat.m_z=t_z1;
		}else{
			var t_t=1.0/(t_fd1+t_fd2)*t_fd1;
			t_quat=c_Quaternion.m_Slerp(t_x1,t_y1,t_z1,t_w1,t_x2,t_y2,t_z2,t_w2,t_t);
		}
	}
	return t_quat;
}
function c_FloatBuffer(){
	Object.call(this);
	this.m_buf=null;
}
function c_TVertexAnim(){
	c_FloatBuffer.call(this);
}
c_TVertexAnim.prototype=extend_class(c_FloatBuffer);
function bb_functions_UpdateWorld(t_anim_speed){
	c_TRender.m_UpdateWorld(t_anim_speed);
	return 0;
}
function c_Enumerator10(){
	Object.call(this);
	this.m_stack=null;
	this.m_index=0;
}
c_Enumerator10.m_new=function(t_stack){
	this.m_stack=t_stack;
	return this;
}
c_Enumerator10.m_new2=function(){
	return this;
}
c_Enumerator10.prototype.p_HasNext=function(){
	return this.m_index<this.m_stack.p_Length();
}
c_Enumerator10.prototype.p_NextObject=function(){
	this.m_index+=1;
	return this.m_stack.m_data[this.m_index-1];
}
function c_Enumerator11(){
	Object.call(this);
	this.m__list=null;
	this.m__curr=null;
}
c_Enumerator11.m_new=function(t_list){
	this.m__list=t_list;
	this.m__curr=t_list.m__head.m__succ;
	return this;
}
c_Enumerator11.m_new2=function(){
	return this;
}
c_Enumerator11.prototype.p_HasNext=function(){
	while(this.m__curr.m__succ.m__pred!=this.m__curr){
		this.m__curr=this.m__curr.m__succ;
	}
	return this.m__curr!=this.m__list.m__head;
}
c_Enumerator11.prototype.p_NextObject=function(){
	var t_data=this.m__curr.m__data;
	this.m__curr=this.m__curr.m__succ;
	return t_data;
}
function c_TShader(){
	c_TBrush.call(this);
	this.m_active=0;
	this.m_override=false;
	this.m_shader_id=0;
	this.m_vertex_id=0;
	this.m_fragment_id=0;
}
c_TShader.prototype=extend_class(c_TBrush);
c_TShader.m_process_list=null;
c_TShader.m_PreProcess=function(t_cam){
	var t_=c_TShader.m_process_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_sh=t_.p_NextObject();
		object_implements((t_sh),"c_IShaderProcess").p_PreProcess(t_cam);
	}
	return 0;
}
c_TShader.m_g_shader=null;
c_TShader.m_PostProcess=function(t_cam){
	var t_=c_TShader.m_process_list.p_ObjectEnumerator();
	while(t_.p_HasNext()){
		var t_sh=t_.p_NextObject();
		object_implements((t_sh),"c_IShaderProcess").p_PostProcess(t_cam);
	}
	return 0;
}
c_TShader.m_default_shader=null;
c_TShader.m_DefaultShader=function(){
	if(!((c_TShader.m_g_shader)!=null)){
		return null;
	}
	c_TShader.m_g_shader.m_active=0;
	c_TShader.m_g_shader=c_TShader.m_default_shader;
	c_TShader.m_g_shader.m_active=1;
	return c_TShader.m_default_shader;
}
c_TShader.prototype.p_Update12=function(){
	return 0;
}
c_TShader.m_new=function(){
	c_TBrush.m_new.call(this);
	return this;
}
c_TShader.prototype.p_ResetShader=function(){
}
c_TShader.prototype.p_CompileShader=function(t_source,t_type){
}
c_TShader.m_LoadDefaultShader=function(t_vp_file,t_fp_file){
	return null;
}
c_TShader.m_SetShader=function(t_sh){
	if((c_TShader.m_g_shader)!=null){
		c_TShader.m_g_shader.m_active=0;
	}
	c_TShader.m_g_shader=t_sh;
	c_TShader.m_g_shader.m_active=1;
	return 0;
}
c_TShader.m_LoadDefaultShader2=function(t_sh){
	c_TShader.m_default_shader=t_sh;
	c_TShader.m_SetShader(c_TShader.m_default_shader);
}
c_TShader.prototype.p_Copy=function(){
}
function c_List13(){
	Object.call(this);
	this.m__head=(c_HeadNode13.m_new.call(new c_HeadNode13));
}
c_List13.m_new=function(){
	return this;
}
c_List13.prototype.p_AddLast13=function(t_data){
	return c_Node16.m_new.call(new c_Node16,this.m__head,this.m__head.m__pred,t_data);
}
c_List13.m_new2=function(t_data){
	var t_=t_data;
	var t_2=0;
	while(t_2<t_.length){
		var t_t=t_[t_2];
		t_2=t_2+1;
		this.p_AddLast13(t_t);
	}
	return this;
}
c_List13.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator12.m_new.call(new c_Enumerator12,this);
}
function c_Node16(){
	Object.call(this);
	this.m__succ=null;
	this.m__pred=null;
	this.m__data=null;
}
c_Node16.m_new=function(t_succ,t_pred,t_data){
	this.m__succ=t_succ;
	this.m__pred=t_pred;
	this.m__succ.m__pred=this;
	this.m__pred.m__succ=this;
	this.m__data=t_data;
	return this;
}
c_Node16.m_new2=function(){
	return this;
}
function c_HeadNode13(){
	c_Node16.call(this);
}
c_HeadNode13.prototype=extend_class(c_Node16);
c_HeadNode13.m_new=function(){
	c_Node16.m_new2.call(this);
	this.m__succ=(this);
	this.m__pred=(this);
	return this;
}
function c_Enumerator12(){
	Object.call(this);
	this.m__list=null;
	this.m__curr=null;
}
c_Enumerator12.m_new=function(t_list){
	this.m__list=t_list;
	this.m__curr=t_list.m__head.m__succ;
	return this;
}
c_Enumerator12.m_new2=function(){
	return this;
}
c_Enumerator12.prototype.p_HasNext=function(){
	while(this.m__curr.m__succ.m__pred!=this.m__curr){
		this.m__curr=this.m__curr.m__succ;
	}
	return this.m__curr!=this.m__list.m__head;
}
c_Enumerator12.prototype.p_NextObject=function(){
	var t_data=this.m__curr.m__data;
	this.m__curr=this.m__curr.m__succ;
	return t_data;
}
function c_Enumerator13(){
	Object.call(this);
	this.m__list=null;
	this.m__curr=null;
}
c_Enumerator13.m_new=function(t_list){
	this.m__list=t_list;
	this.m__curr=t_list.m__head.m__succ;
	return this;
}
c_Enumerator13.m_new2=function(){
	return this;
}
c_Enumerator13.prototype.p_HasNext=function(){
	while(this.m__curr.m__succ.m__pred!=this.m__curr){
		this.m__curr=this.m__curr.m__succ;
	}
	return this.m__curr!=this.m__list.m__head;
}
c_Enumerator13.prototype.p_NextObject=function(){
	var t_data=this.m__curr.m__data;
	this.m__curr=this.m__curr.m__succ;
	return t_data;
}
function c_RenderAlphaList(){
	c_List3.call(this);
}
c_RenderAlphaList.prototype=extend_class(c_List3);
c_RenderAlphaList.m_new=function(){
	c_List3.m_new.call(this);
	return this;
}
c_RenderAlphaList.prototype.p_Compare2=function(t_left,t_right){
	if(t_left.m_alpha_order>t_right.m_alpha_order){
		return -1;
	}
	return ((t_left.m_alpha_order<t_right.m_alpha_order)?1:0);
}
function c_Enumerator14(){
	Object.call(this);
	this.m__list=null;
	this.m__curr=null;
}
c_Enumerator14.m_new=function(t_list){
	this.m__list=t_list;
	this.m__curr=t_list.m__head.m__succ;
	return this;
}
c_Enumerator14.m_new2=function(){
	return this;
}
c_Enumerator14.prototype.p_HasNext=function(){
	while(this.m__curr.m__succ.m__pred!=this.m__curr){
		this.m__curr=this.m__curr.m__succ;
	}
	return this.m__curr!=this.m__list.m__head;
}
c_Enumerator14.prototype.p_NextObject=function(){
	var t_data=this.m__curr.m__data;
	this.m__curr=this.m__curr.m__succ;
	return t_data;
}
function bb_functions_RenderWorld(){
	c_TRender.m_RenderWorld();
	return 0;
}
function c_EffectState(){
	Object.call(this);
	this.m_use_full_bright=0;
	this.m_use_vertex_colors=0;
	this.m_use_flatshade=0;
	this.m_use_fog=1;
	this.m_ambient=[1.0,1.0,1.0,1.0];
	this.m_diffuse=[1.0,1.0,1.0,1.0];
	this.m_specular=[1.0,1.0,1.0,1.0];
	this.m_shininess=[100.0];
	this.m_use_depth_test=0;
	this.m_use_depth_write=0;
	this.m_use_backface_culling=0;
	this.m_use_alpha_test=0;
	this.m_use_perpixellighting=0;
	this.m_red=.0;
	this.m_green=.0;
	this.m_blue=.0;
	this.m_alpha=.0;
	this.m_shine=.0;
	this.m_blend=0;
	this.m_fx=0;
	this.m_num_tex=0;
	this.m_tex=[];
}
c_EffectState.m_new=function(){
	return this;
}
c_EffectState.prototype.p_SetNull=function(){
	this.m_use_full_bright=-1;
	this.m_use_vertex_colors=-1;
	this.m_use_flatshade=-1;
	this.m_use_fog=-1;
	this.m_ambient=[-1.0,-1.0,-1.0,1.0];
	this.m_diffuse=[-1.0,-1.0,-1.0,1.0];
	this.m_specular=[-1.0,-1.0,-1.0,1.0];
	this.m_shininess=[-1.0];
	this.m_use_depth_test=-1;
	this.m_use_depth_write=-1;
	this.m_use_backface_culling=-1;
	this.m_use_alpha_test=0;
	this.m_use_perpixellighting=0;
	this.m_red=-1.0;
	this.m_green=-1.0;
	this.m_blue=-1.0;
	this.m_alpha=-1.0;
	this.m_shine=-1.0;
	this.m_blend=99999;
	this.m_fx=99999;
	this.m_num_tex=-1;
}
c_EffectState.prototype.p_UpdateEffect=function(t_surf,t_ent,t_cam){
	var t_ambient_red=.0;
	var t_ambient_green=.0;
	var t_ambient_blue=.0;
	this.m_red=t_ent.m_brush.m_red;
	this.m_green=t_ent.m_brush.m_green;
	this.m_blue=t_ent.m_brush.m_blue;
	this.m_alpha=t_ent.m_brush.m_alpha;
	this.m_shine=t_ent.m_brush.m_shine;
	this.m_blend=t_ent.m_brush.m_blend;
	this.m_fx=t_ent.m_brush.m_fx;
	this.m_num_tex=t_ent.m_brush.m_no_texs;
	this.m_tex=t_ent.m_brush.m_tex;
	if((t_surf.m_brush)!=null){
		var t_shine2=0.0;
		this.m_red=this.m_red*t_surf.m_brush.m_red;
		this.m_green=this.m_green*t_surf.m_brush.m_green;
		this.m_blue=this.m_blue*t_surf.m_brush.m_blue;
		this.m_alpha=this.m_alpha*t_surf.m_brush.m_alpha;
		t_shine2=t_surf.m_brush.m_shine;
		if(this.m_shine==0.0){
			this.m_shine=t_shine2;
		}
		if(this.m_shine!=0.0 && t_shine2!=0.0){
			this.m_shine=this.m_shine*t_shine2;
		}
		if(this.m_blend==0){
			this.m_blend=t_surf.m_brush.m_blend;
		}
		this.m_fx=this.m_fx|t_surf.m_brush.m_fx;
		if(this.m_num_tex<t_surf.m_brush.m_no_texs){
			this.m_num_tex=t_surf.m_brush.m_no_texs;
			this.m_tex=t_surf.m_brush.m_tex;
		}
	}
	this.m_alpha=this.m_alpha-t_ent.m_fade_alpha;
	this.m_use_depth_test=1;
	this.m_use_depth_write=1;
	var t_enable_blend=0;
	if(t_ent.m_using_alpha==true){
		if(t_ent.m_brush.m_alpha<1.0){
			t_enable_blend=1;
			this.m_use_depth_write=0;
		}else{
			if(t_surf.m_alpha_enable==true){
				t_enable_blend=1;
				this.m_use_depth_write=0;
			}else{
				t_enable_blend=0;
				this.m_use_depth_test=1;
				this.m_use_depth_write=1;
			}
		}
	}else{
		t_enable_blend=0;
	}
	if(t_enable_blend==0){
		this.m_blend=-1;
	}
	this.m_use_vertex_colors=0;
	this.m_use_flatshade=0;
	this.m_use_fog=((t_cam.m_fog_mode>0)?1:0);
	if((this.m_fx&1)!=0){
		t_ambient_red=0.0;
		t_ambient_green=0.0;
		t_ambient_blue=0.0;
		this.m_use_full_bright=1;
	}else{
		t_ambient_red=c_TLight.m_ambient_red;
		t_ambient_green=c_TLight.m_ambient_green;
		t_ambient_blue=c_TLight.m_ambient_blue;
		this.m_use_full_bright=0;
	}
	if((this.m_fx&2)!=0){
		this.m_use_vertex_colors=1;
		this.m_red=1.0;
		this.m_green=1.0;
		this.m_blue=1.0;
		this.m_alpha=1.0;
	}
	if((this.m_fx&4)!=0){
		this.m_use_flatshade=1;
	}
	if((this.m_fx&8)!=0){
		this.m_use_fog=0;
	}
	if((this.m_fx&16)!=0){
		this.m_use_backface_culling=0;
	}else{
		this.m_use_backface_culling=1;
	}
	if((this.m_fx&64)!=0){
		this.m_use_depth_test=0;
		this.m_use_depth_write=0;
	}
	this.m_use_alpha_test=0;
	var t_tex_alphatest=0;
	if(this.m_num_tex>0){
		for(var t_i=0;t_i<=this.m_num_tex-1;t_i=t_i+1){
			if((this.m_tex[t_i])!=null){
				t_tex_alphatest|=this.m_tex[t_i].m_flags&4;
			}
		}
	}
	if((this.m_fx&128)>0 || t_tex_alphatest>0){
		this.m_use_alpha_test=1;
		this.m_use_depth_test=1;
		this.m_use_depth_write=1;
	}
	if((this.m_fx&256)!=0){
		this.m_use_perpixellighting=1;
	}
	this.m_ambient=[t_ambient_red,t_ambient_green,t_ambient_blue,0.0];
	this.m_diffuse=[this.m_red,this.m_green,this.m_blue,this.m_alpha];
	this.m_specular=[this.m_shine,this.m_shine,this.m_shine,1.0];
	this.m_shininess=[100.0];
	if((t_cam.m_draw2D)!=0){
		if((this.m_fx&64)==0){
			this.m_use_depth_test=0;
			this.m_use_depth_write=0;
		}
		this.m_use_fog=0;
		this.m_use_full_bright=1;
	}
}
c_EffectState.prototype.p_Overwrite4=function(t_e){
	this.m_use_full_bright=t_e.m_use_full_bright;
	this.m_use_vertex_colors=t_e.m_use_vertex_colors;
	this.m_use_flatshade=t_e.m_use_flatshade;
	this.m_use_fog=t_e.m_use_fog;
	this.m_ambient=[t_e.m_ambient[0],t_e.m_ambient[1],t_e.m_ambient[2],t_e.m_ambient[3]];
	this.m_diffuse=[t_e.m_diffuse[0],t_e.m_diffuse[1],t_e.m_diffuse[2],t_e.m_diffuse[3]];
	this.m_specular=[t_e.m_specular[0],t_e.m_specular[1],t_e.m_specular[2],t_e.m_specular[3]];
	this.m_shininess=[t_e.m_shininess[0]];
	this.m_use_depth_test=t_e.m_use_depth_test;
	this.m_use_depth_write=t_e.m_use_depth_write;
	this.m_use_backface_culling=t_e.m_use_backface_culling;
	this.m_red=t_e.m_red;
	this.m_green=t_e.m_green;
	this.m_blue=t_e.m_blue;
	this.m_alpha=t_e.m_alpha;
	this.m_shine=t_e.m_shine;
	this.m_blend=t_e.m_blend;
	this.m_fx=t_e.m_fx;
	this.m_use_alpha_test=t_e.m_use_alpha_test;
	this.m_use_perpixellighting=t_e.m_use_perpixellighting;
	this.m_num_tex=t_e.m_num_tex;
}
function c_TShaderGLSL(){
	c_TShader.call(this);
	this.m_MAX_TEXTURES=1;
	this.m_u=null;
	this.m_MAX_LIGHTS=1;
	this.m_webgl_shader=0;
	this.m_use_base_variables=1;
}
c_TShaderGLSL.prototype=extend_class(c_TShader);
c_TShaderGLSL.m_new=function(){
	c_TShader.m_new.call(this);
	return this;
}
c_TShaderGLSL.prototype.p_ResetShader=function(){
	error("** Must extend ResetShader for context loss:"+this.m_name);
	return 0;
}
c_TShaderGLSL.prototype.p_CompileShader=function(t_source,t_type){
	if(t_source.length<2){
		print("**Shader file not found");
		return 0;
	}
	var t_id=gl.createShader(t_type);
	this.m_webgl_shader=t_id;
	if(t_type==35633){
		this.m_vertex_id=t_id;
	}
	if(t_type==35632){
		this.m_fragment_id=t_id;
	}
	gl.shaderSource(t_id,t_source);
	gl.compileShader(t_id);
	var t_result=new_number_array(1);
	_glGetShaderiv(t_id,35713,t_result);
	if(t_result[0]!=1){
		var t_log="";
		t_log=gl.getShaderInfoLog(t_id);
		print(t_log);
		print("**Shader Compile Error "+String(t_result[0]));
		gl.deleteShader(t_id);
		if(t_type==35633){
			this.m_vertex_id=0;
		}else{
			this.m_fragment_id=0;
		}
		return 0;
	}
	return 1;
}
c_TShaderGLSL.m_GetGLError=function(){
	var t_gle=gl.getError();
	if(t_gle!=0){
		print("*glerror: "+String(t_gle));
		return 1;
	}
	return 0;
}
c_TShaderGLSL.prototype.p_LinkVariables=function(){
	if((this.m_active)!=0){
		this.m_use_base_variables=1;
		this.m_active=this.m_u.p_Link(this.m_shader_id);
		if((c_TShaderGLSL.m_GetGLError())!=0){
			bb_tutility_Dprint("** uniform assignment","","","","","");
		}
	}
	return 0;
}
c_TShaderGLSL.prototype.p_LinkShader=function(){
	this.m_shader_id=gl.createProgram();
	if(this.m_shader_id<0){
		print("**Shader Program not created.");
	}
	gl.attachShader(this.m_shader_id,this.m_vertex_id);
	gl.attachShader(this.m_shader_id,this.m_fragment_id);
	gl.linkProgram(this.m_shader_id);
	var t_result=new_number_array(1);
	_glGetProgramiv(this.m_shader_id,35714,t_result);
	var t_log="";
	t_log=gl.getProgramInfoLog(this.m_shader_id);
	if(t_result[0]!=1){
		print("**Shader Linking Error ");
		gl.deleteShader(this.m_vertex_id);
		gl.deleteShader(this.m_fragment_id);
		gl.deleteProgram(this.m_shader_id);
		this.m_vertex_id=0;
		this.m_fragment_id=0;
		this.m_shader_id=0;
		return 0;
	}
	this.m_active=1;
	this.m_u=c_ShaderUniforms.m_new.call(new c_ShaderUniforms);
	this.p_LinkVariables();
	return 1;
}
c_TShaderGLSL.prototype.p_Copy=function(){
	var t_brush=c_TShaderGLSL.m_new.call(new c_TShaderGLSL);
	t_brush.m_no_texs=this.m_no_texs;
	t_brush.m_name=this.m_name;
	t_brush.m_red=this.m_red;
	t_brush.m_green=this.m_green;
	t_brush.m_blue=this.m_blue;
	t_brush.m_alpha=this.m_alpha;
	t_brush.m_shine=this.m_shine;
	t_brush.m_blend=this.m_blend;
	t_brush.m_fx=this.m_fx;
	t_brush.m_tex[0]=this.m_tex[0];
	t_brush.m_tex[1]=this.m_tex[1];
	t_brush.m_tex[2]=this.m_tex[2];
	t_brush.m_tex[3]=this.m_tex[3];
	t_brush.m_tex[4]=this.m_tex[4];
	t_brush.m_tex[5]=this.m_tex[5];
	t_brush.m_tex[6]=this.m_tex[6];
	t_brush.m_tex[7]=this.m_tex[7];
	t_brush.m_shader_id=this.m_shader_id;
	t_brush.m_fragment_id=this.m_fragment_id;
	t_brush.m_vertex_id=this.m_vertex_id;
	t_brush.m_u=this.m_u;
	t_brush.m_override=this.m_override;
	t_brush.m_active=1;
	t_brush.p_LinkVariables();
	t_brush.m_active=this.m_active;
	t_brush.m_use_base_variables=this.m_use_base_variables;
	return (t_brush);
}
function c_SurfaceAlphaList(){
	c_List2.call(this);
}
c_SurfaceAlphaList.prototype=extend_class(c_List2);
c_SurfaceAlphaList.m_new=function(){
	c_List2.m_new.call(this);
	return this;
}
function c_FullShader(){
	c_TShaderGLSL.call(this);
}
c_FullShader.prototype=extend_class(c_TShaderGLSL);
c_FullShader.m_shader=[];
c_FullShader.m_GetShader=function(t_ppl,t_num_lights,t_num_texs){
	return c_FullShader.m_shader[2*t_num_texs+t_ppl];
}
c_FullShader.m_fastbrightshader=null;
c_FullShader.m_init_id=0;
c_FullShader.prototype.p_ResetShader=function(){
	if((c_FullShader.m_shader[0])!=null){
		for(var t_i=0;t_i<=9;t_i=t_i+1){
			c_FullShader.m_shader[t_i].m_shader_id=0;
		}
	}
	c_FullShader.m_shader[0]=(c_MultiShader.m_new.call(new c_MultiShader,0,1,0,false));
	c_FullShader.m_shader[1]=(c_MultiShader.m_new.call(new c_MultiShader,1,1,0,false));
	c_FullShader.m_shader[2]=(c_MultiShader.m_new.call(new c_MultiShader,0,1,1,false));
	c_FullShader.m_shader[3]=(c_MultiShader.m_new.call(new c_MultiShader,1,1,1,false));
	c_FullShader.m_shader[4]=(c_MultiShader.m_new.call(new c_MultiShader,0,1,2,false));
	c_FullShader.m_shader[5]=(c_MultiShader.m_new.call(new c_MultiShader,1,1,2,false));
	c_FullShader.m_shader[6]=(c_MultiShader.m_new.call(new c_MultiShader,0,1,3,false));
	c_FullShader.m_shader[7]=(c_MultiShader.m_new.call(new c_MultiShader,1,1,3,false));
	c_FullShader.m_shader[8]=(c_MultiShader.m_new.call(new c_MultiShader,0,1,4,false));
	c_FullShader.m_shader[9]=(c_MultiShader.m_new.call(new c_MultiShader,1,1,4,false));
	c_FullShader.m_init_id=c_FullShader.m_shader[0].m_shader_id;
	this.m_shader_id=c_FullShader.m_shader[0].m_shader_id;
	if((c_FullShader.m_shader[0].m_active)!=0){
		bb_tutility_Dprint("..FullShader success","","","","","");
	}
	this.m_active=c_FullShader.m_shader[0].m_active;
	if((c_FullShader.m_fastbrightshader)!=null){
		c_FastBrightShader.m_init_id=0;
		c_FullShader.m_fastbrightshader.m_shader_id=0;
	}
	c_FullShader.m_fastbrightshader=(c_FastBrightShader.m_new.call(new c_FastBrightShader));
	return 0;
}
c_FullShader.m_global_uniforms=null;
c_FullShader.m_new=function(){
	c_TShaderGLSL.m_new.call(this);
	this.m_MAX_TEXTURES=4;
	this.m_MAX_LIGHTS=1;
	this.m_name="FullShader";
	if(c_FullShader.m_init_id==0 && this.m_shader_id==0){
		this.p_ResetShader();
	}else{
		if((c_FullShader.m_init_id)!=0){
			this.m_shader_id=c_FullShader.m_init_id;
			this.m_u=c_FullShader.m_global_uniforms;
			this.m_active=1;
		}
	}
	return this;
}
c_FullShader.prototype.p_Update12=function(){
	return 0;
}
function c_ShaderUniforms(){
	Object.call(this);
	this.m_vertcoords=0;
	this.m_normals=0;
	this.m_colors=0;
	this.m_flags=0;
	this.m_colorflag=0;
	this.m_lightflag=0;
	this.m_ambient_color=0;
	this.m_base_color=0;
	this.m_shininess=0;
	this.m_alphaflag=0;
	this.m_fogflag=0;
	this.m_fog_color=0;
	this.m_fog_range=0;
	this.m_texture=new_number_array(9);
	this.m_tex_position=new_number_array(9);
	this.m_tex_rotation=new_number_array(9);
	this.m_tex_scale=new_number_array(9);
	this.m_tex_blend=new_number_array(9);
	this.m_texfx_normal=[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1];
	this.m_vertCoordSet=new_number_array(9);
	this.m_texcoords0=0;
	this.m_texcoords1=0;
	this.m_texflag=0;
	this.m_light_type=new_number_array(9);
	this.m_light_matrix=new_number_array(9);
	this.m_light_att=new_number_array(9);
	this.m_light_color=new_number_array(9);
	this.m_light_spot=new_number_array(9);
	this.m_m_matrix=0;
	this.m_v_matrix=0;
	this.m_p_matrix=0;
	this.m_scaleInv=0;
	this.m_lightpMatrix=0;
	this.m_lightvMatrix=0;
}
c_ShaderUniforms.m_new=function(){
	return this;
}
c_ShaderUniforms.prototype.p_Link=function(t_shader_id){
	this.m_vertcoords=gl.getAttribLocation(t_shader_id,"aVertcoords");
	this.m_texcoords0=gl.getAttribLocation(t_shader_id,"aTexcoords0");
	this.m_texcoords1=gl.getAttribLocation(t_shader_id,"aTexcoords1");
	this.m_normals=gl.getAttribLocation(t_shader_id,"aNormals");
	this.m_colors=gl.getAttribLocation(t_shader_id,"aColors");
	this.m_p_matrix=_glGetUniformLocation(t_shader_id,"pMatrix");
	this.m_m_matrix=_glGetUniformLocation(t_shader_id,"mMatrix");
	this.m_v_matrix=_glGetUniformLocation(t_shader_id,"vMatrix");
	for(var t_i=0;t_i<=7;t_i=t_i+1){
		this.m_light_type[t_i]=_glGetUniformLocation(t_shader_id,"lightType["+String(t_i)+"]");
		this.m_light_matrix[t_i]=_glGetUniformLocation(t_shader_id,"lightMatrix["+String(t_i)+"]");
		this.m_light_color[t_i]=_glGetUniformLocation(t_shader_id,"lightColor["+String(t_i)+"]");
		this.m_light_att[t_i]=_glGetUniformLocation(t_shader_id,"lightAtt["+String(t_i)+"]");
		this.m_light_spot[t_i]=_glGetUniformLocation(t_shader_id,"lightSpot["+String(t_i)+"]");
	}
	this.m_base_color=_glGetUniformLocation(t_shader_id,"basecolor");
	this.m_ambient_color=_glGetUniformLocation(t_shader_id,"ambientcolor");
	this.m_shininess=_glGetUniformLocation(t_shader_id,"shininess");
	this.m_fog_color=_glGetUniformLocation(t_shader_id,"fogColor");
	this.m_fog_range=_glGetUniformLocation(t_shader_id,"fogRange");
	this.m_flags=_glGetUniformLocation(t_shader_id,"flags");
	this.m_texflag=_glGetUniformLocation(t_shader_id,"texflag");
	this.m_colorflag=_glGetUniformLocation(t_shader_id,"colorflag");
	this.m_lightflag=_glGetUniformLocation(t_shader_id,"lightflag");
	this.m_fogflag=_glGetUniformLocation(t_shader_id,"fogflag");
	this.m_alphaflag=_glGetUniformLocation(t_shader_id,"alphaflag");
	this.m_lightpMatrix=_glGetUniformLocation(t_shader_id,"lightpMatrix");
	this.m_lightvMatrix=_glGetUniformLocation(t_shader_id,"lightvMatrix");
	this.m_scaleInv=_glGetUniformLocation(t_shader_id,"scaleInv");
	for(var t_i2=0;t_i2<=7;t_i2=t_i2+1){
		this.m_texture[t_i2]=_glGetUniformLocation(t_shader_id,"uTexture["+String(t_i2)+"]");
		this.m_tex_position[t_i2]=_glGetUniformLocation(t_shader_id,"texPosition["+String(t_i2)+"]");
		this.m_tex_scale[t_i2]=_glGetUniformLocation(t_shader_id,"texScale["+String(t_i2)+"]");
		this.m_tex_rotation[t_i2]=_glGetUniformLocation(t_shader_id,"texRotation["+String(t_i2)+"]");
		this.m_tex_blend[t_i2]=_glGetUniformLocation(t_shader_id,"texBlend["+String(t_i2)+"]");
		this.m_vertCoordSet[t_i2]=_glGetUniformLocation(t_shader_id,"vertCoordSet["+String(t_i2)+"]");
	}
	this.m_texfx_normal[0]=_glGetUniformLocation(t_shader_id,"texfxNormal[0]");
	this.m_texfx_normal[1]=_glGetUniformLocation(t_shader_id,"texfxNormal[1]");
	if(this.m_vertcoords<0){
		bb_tutility_Dprint("**uniform assignment error: vertcoords does not exist","","","","","");
		return 0;
	}
	return 1;
}
function c_TPixmapGL(){
	c_TPixmap.call(this);
	this.m_pixels=null;
	this.m_format=0;
	this.m_pitch=0;
	this.implments={c_IPixmapManager:1};
}
c_TPixmapGL.prototype=extend_class(c_TPixmap);
c_TPixmapGL.m_new=function(){
	c_TPixmap.m_new.call(this);
	return this;
}
c_TPixmapGL.m_Init=function(){
	if(!((c_TPixmap.m_manager)!=null)){
		c_TPixmap.m_manager=(c_TPixmapGL.m_new.call(new c_TPixmapGL));
	}
	if(!((c_TPixmap.m_preloader)!=null)){
		c_TPixmap.m_preloader=c_TPixmapPreloader.m_new.call(new c_TPixmapPreloader,(c_PreloadHTML.m_new.call(new c_PreloadHTML)));
	}
	return 0;
}
c_TPixmapGL.prototype.p_ResizePixmap=function(t_neww,t_newh){
	var t_newpix=c_TPixmapGL.m_new.call(new c_TPixmapGL);
	if(t_neww<1 || t_newh<1){
		return (t_newpix);
	}
	t_newpix.m_pixels=HTMLResizePixmap(this.m_pixels,t_neww,t_newh,true);
	t_newpix.m_width=t_neww;
	t_newpix.m_height=t_newh;
	return (t_newpix);
}
c_TPixmapGL.prototype.p_ResizePixmapNoSmooth=function(t_neww,t_newh){
	var t_newpix=c_TPixmapGL.m_new.call(new c_TPixmapGL);
	if(t_neww<1 || t_newh<1){
		return (t_newpix);
	}
	t_newpix.m_pixels=HTMLResizePixmap(this.m_pixels,t_neww,t_newh,false);
	t_newpix.m_width=t_neww;
	t_newpix.m_height=t_newh;
	return (t_newpix);
}
c_TPixmapGL.prototype.p_LoadPixmap=function(t_f){
	var t_p=c_TPixmapGL.m_new.call(new c_TPixmapGL);
	var t_info=new_number_array(3);
	c_TPixmap.m_preloader.p_GetPixmapPreLoad((t_p),t_f);
	t_p.m_format=4;
	if((t_p.m_width)!=0){
		t_p.m_pitch=t_p.m_width;
	}
	if(!((t_p.m_width)!=0) && !((t_p.m_height)!=0) || !((t_p.m_pixels)!=null)){
		bb_tutility_Dprint("**Image Not Preloaded: "+t_f,"","","","","");
	}
	return (t_p);
}
c_TPixmapGL.prototype.p_CreatePixmap=function(t_w,t_h,t_format){
	var t_p=c_TPixmapGL.m_new.call(new c_TPixmapGL);
	t_p.m_pixels=CreateImageData(t_w,t_h);
	t_p.m_width=t_w;
	t_p.m_height=t_h;
	t_p.m_format=t_format;
	t_p.m_pitch=t_w;
	return (t_p);
}
c_TPixmapGL.prototype.p_MaskPixmap=function(t_r,t_g,t_b){
	this.m_pixels=HTMLMaskPixmap(this.m_pixels,t_r,t_g,t_b);
}
c_TPixmapGL.prototype.p_FreePixmap=function(){
	this.m_pixels=null;
}
function c_PreloadHTML(){
	Object.call(this);
	this.m_p_map=c_ArrayIntMap.m_new.call(new c_ArrayIntMap);
	this.implments={c_IPreloadManager:1};
}
c_PreloadHTML.m_new=function(){
	return this;
}
c_PreloadHTML.prototype.p_IsLoaded=function(t_file_id){
	var t_f=this.m_p_map.p_Get2(t_file_id);
	if((t_f)!=null){
		return CheckIsLoaded(t_f.m_data);
	}
	return false;
}
c_PreloadHTML.prototype.p_PreLoadData=function(t_f,t_id){
	if(t_id<1){
		return;
	}
	t_f=bb_data_FixDataPath(t_f);
	t_f=t_f.split("//")[1];
	var t_d=c_PreloadData.m_new.call(new c_PreloadData);
	t_d.m_id=t_id;
	t_d.m_data=LoadImageData(t_f,t_id);
	this.m_p_map.p_Set3(t_id,t_d);
}
c_PreloadHTML.prototype.p_SetPixmapFromID=function(t_pixmap,t_id,t_f){
	var t_p=object_downcast((t_pixmap),c_TPixmapGL);
	if((t_p)!=null){
		if(t_id>0){
			var t_d=this.m_p_map.p_Get2(t_id);
			if((t_d)!=null){
				t_p.m_pixels=t_d.m_data;
				var t_info=GetImageInfo(t_p.m_pixels);
				t_p.m_width=t_info[0];
				t_p.m_height=t_info[1];
			}
		}
	}
}
function c_FrameBuffer(){
	Object.call(this);
}
function c_FrameBufferGL(){
	c_FrameBuffer.call(this);
	this.m_fbufferID=0;
	this.m_texture=null;
	this.m_depth_flag=0;
	this.m_rbufferID=0;
	this.m_UVW=1.0;
	this.m_UVH=1.0;
}
c_FrameBufferGL.prototype=extend_class(c_FrameBuffer);
c_FrameBufferGL.m_supportFBO=0;
c_FrameBufferGL.m_fboStack=null;
c_FrameBufferGL.m_gsurf=null;
c_FrameBufferGL.m_BindFBO=function(t_fbo){
	t_fbo.m_fbufferID=gl.createFramebuffer();
	_glBindTexture(3553,t_fbo.m_texture.m_gltex[0]);
	if(!t_fbo.m_texture.m_tex_smooth){
		gl.texParameteri(3553,10240,9728);
		gl.texParameteri(3553,10241,9728);
	}else{
		gl.texParameteri(3553,10240,9729);
		gl.texParameteri(3553,10241,9729);
	}
	gl.texParameteri(3553,10242,33071);
	gl.texParameteri(3553,10243,33071);
	_glBindFramebuffer(36160,t_fbo.m_fbufferID);
	gl.framebufferTexture2D(36160,36064,3553,t_fbo.m_texture.m_gltex[0],0);
	if(((t_fbo.m_depth_flag)!=0) && !((t_fbo.m_rbufferID)!=0)){
		t_fbo.m_rbufferID=gl.createRenderbuffer();
	}
	if((t_fbo.m_depth_flag)!=0){
		_glBindRenderbuffer(36161,t_fbo.m_rbufferID);
		gl.renderbufferStorage(36161,33189,t_fbo.m_texture.m_width,t_fbo.m_texture.m_height);
		gl.framebufferRenderbuffer(36160,36096,36161,t_fbo.m_rbufferID);
	}
	var t_status=gl.checkFramebufferStatus(36160);
	var t_1=t_status;
	if(t_1==36053){
		bb_tutility_Dprint("..FBO success","","","","","");
	}else{
		if(t_1==36061){
			bb_tutility_Dprint("**FBO: unsupported.","","","","","");
		}else{
			bb_tutility_Dprint("**FBO unsuccessful :"+String(t_status),"","","","","");
		}
	}
	if(t_fbo.m_texture.m_width>t_fbo.m_texture.m_height){
		t_fbo.m_UVW=(t_fbo.m_texture.m_height)/(t_fbo.m_texture.m_width);
		t_fbo.m_UVH=1.0;
	}else{
		if(t_fbo.m_texture.m_width<t_fbo.m_texture.m_height){
			t_fbo.m_UVW=1.0;
			t_fbo.m_UVH=(t_fbo.m_texture.m_width)/(t_fbo.m_texture.m_height);
		}else{
			t_fbo.m_UVW=1.0;
			t_fbo.m_UVH=1.0;
		}
	}
	if(t_fbo.m_texture.m_width<c_TRender.m_width || t_fbo.m_texture.m_height<c_TRender.m_height){
		if(c_TRender.m_width>c_TRender.m_height){
			t_fbo.m_UVH=(c_TRender.m_height)/(c_TRender.m_width);
		}else{
			t_fbo.m_UVW=(c_TRender.m_width)/(c_TRender.m_height);
		}
	}else{
		t_fbo.m_UVW=(c_TRender.m_width)/(t_fbo.m_texture.m_width);
		t_fbo.m_UVH=(c_TRender.m_height)/(t_fbo.m_texture.m_height);
	}
	if(!((c_FrameBufferGL.m_gsurf)!=null)){
		c_FrameBufferGL.m_gsurf=c_TSurface.m_new.call(new c_TSurface);
		c_FrameBufferGL.m_gsurf.p_AddVertex(-1.0,1.0,0.0,0.0,1.0,0.0);
		c_FrameBufferGL.m_gsurf.p_AddVertex(-1.0,-1.0,0.0,0.0,0.0,0.0);
		c_FrameBufferGL.m_gsurf.p_AddVertex(1.0,-1.0,0.0,1.0,0.0,0.0);
		c_FrameBufferGL.m_gsurf.p_AddVertex(1.0,1.0,0.0,1.0,1.0,0.0);
		c_FrameBufferGL.m_gsurf.p_AddTriangle(1,0,2);
		c_FrameBufferGL.m_gsurf.p_AddTriangle(2,0,3);
		c_FrameBufferGL.m_gsurf.m_reset_vbo=-1;
	}
	_glBindTexture(3553,0);
	_glBindFramebuffer(36160,0);
	_glBindRenderbuffer(36161,0);
	if(false && ((c_OpenglES20.m_GetGLError())!=0)){
		print("**FBO Create error");
	}
}
c_FrameBufferGL.m_framebuffer_active=null;
function c_MultiShader(){
	c_TShaderGLSL.call(this);
}
c_MultiShader.prototype=extend_class(c_TShaderGLSL);
c_MultiShader.m_VERTVARS0="";
c_MultiShader.m_LightVars="";
c_MultiShader.m_LightingEquation0="";
c_MultiShader.m_VERTP1="";
c_MultiShader.m_VERTP2="";
c_MultiShader.prototype.p_Vert_Texture=function(t_i){
	if(t_i==0){
		return "texcoord[0].xy = all_zeros.xy; ";
	}
	var t_str="vec2 scale; float cosang; float sinang; vec2 pos;";
	t_str=t_str+"/*NORMAL MAPPING ROTATION*//*-- tangent in aColors, cross to find bitangent*/if ((texflag > 0.0) && (texfxNormal[0] > 0.0)) {\tvec3 tangent = normalize(mMatrix*aColors).xyz;\tvec3 bitangent = normalize( cross(  normal.xyz, tangent.xyz ));\tmat3 nmMat = mat3( tangent.x, bitangent.x, normal.x,tangent.y, bitangent.y, normal.y,tangent.z, bitangent.z, normal.z);\tnmLight = nmMat * nmLight;\t}";
	for(var t_j=0;t_j<=t_i-1;t_j=t_j+1){
		t_str=t_str+("texcoord["+String(t_j)+"].xy = mix(aTexcoords0.xy, aTexcoords1.xy, vertCoordSet["+String(t_j)+"]);");
		t_str=t_str+("scale = texScale["+String(t_j)+"]; cosang = texRotation["+String(t_j)+"].x; sinang = texRotation["+String(t_j)+"].y; pos = texPosition["+String(t_j)+"]/scale.xy;"+"(texcoord["+String(t_j)+"]).x = ((texcoord["+String(t_j)+"].x + pos.x) * cosang - (texcoord["+String(t_j)+"].y + pos.y) * sinang)*scale.x;"+"(texcoord["+String(t_j)+"]).y = ((texcoord["+String(t_j)+"].x + pos.x) * sinang + (texcoord["+String(t_j)+"].y + pos.y) * cosang)*scale.y;");
	}
	return t_str;
}
c_MultiShader.m_VERTP3="";
c_MultiShader.m_VERTFOG="";
c_MultiShader.m_Vert_Lighting0="";
c_MultiShader.m_FRAGVARS0="";
c_MultiShader.m_FRAGVARS1="";
c_MultiShader.m_FRAGBLEND="";
c_MultiShader.m_Frag_VertexLighting0="";
c_MultiShader.m_FRAGP2="";
c_MultiShader.prototype.p_Frag_Texture=function(t_i){
	var t_str="";
	var t_clr="";
	if(t_i==0){
		t_str="finalcolor = vertcolor;";
	}
	if(t_i>0){
		t_str="if (!usenormalmap) { texture = texture2D(uTexture[0], (texcoord[0]).xy); finalcolor = BlendFunction(texBlend[0].x, texture, finalcolor, vertcolor); } if(!usenormalmap && (texture.a<alphaflag)) {discard;} /* fix for tegra3 */ \n";
		for(var t_j=1;t_j<=t_i-1;t_j=t_j+1){
			t_str=t_str+(" texture = texture2D(uTexture["+String(t_j)+"], (texcoord["+String(t_j)+"]).xy);");
			t_str=t_str+(" finalcolor = BlendFunction(texBlend["+String(t_j)+"].x, texture, finalcolor, vec4(1,1,1, vertcolor.w) ); ");
		}
	}
	return t_str;
}
c_MultiShader.m_FRAGCOLOR="";
c_MultiShader.m_Frag_LightVars="";
c_MultiShader.m_global_uniforms=null;
c_MultiShader.m_new=function(t_ppl,t_num_lights,t_num_texs,t_debug){
	c_TShaderGLSL.m_new.call(this);
	this.m_MAX_TEXTURES=4;
	this.m_MAX_LIGHTS=1;
	this.m_name="FullShader PPL"+String(t_ppl)+" LI"+String(t_num_lights)+" TX"+String(t_num_texs);
	var t_VP="";
	var t_FP="";
	if(t_ppl==0){
		t_VP=[c_MultiShader.m_VERTVARS0,c_MultiShader.m_LightVars,c_MultiShader.m_LightingEquation0,c_MultiShader.m_VERTP1,c_MultiShader.m_VERTP2,this.p_Vert_Texture(t_num_texs),c_MultiShader.m_VERTP3,c_MultiShader.m_VERTFOG,c_MultiShader.m_Vert_Lighting0,"}\n"].join(t_VP);
		t_FP=[c_MultiShader.m_FRAGVARS0,c_MultiShader.m_FRAGVARS1,c_MultiShader.m_FRAGBLEND,c_MultiShader.m_Frag_VertexLighting0,c_MultiShader.m_FRAGP2,this.p_Frag_Texture(t_num_texs),c_MultiShader.m_FRAGCOLOR].join(t_FP);
	}else{
		t_VP=[c_MultiShader.m_VERTVARS0,c_MultiShader.m_VERTP1,c_MultiShader.m_VERTP2,this.p_Vert_Texture(t_num_texs),c_MultiShader.m_VERTP3,c_MultiShader.m_VERTFOG,"}\n"].join(t_VP);
		t_FP=[c_MultiShader.m_FRAGVARS0,c_MultiShader.m_LightVars,c_MultiShader.m_Frag_LightVars,c_MultiShader.m_FRAGVARS1,c_MultiShader.m_FRAGBLEND,c_MultiShader.m_LightingEquation0,c_MultiShader.m_FRAGP2,this.p_Frag_Texture(t_num_texs),c_MultiShader.m_FRAGCOLOR].join(t_FP);
	}
	if(this.m_shader_id==0 && ((this.p_CompileShader(t_VP,35633))!=0) && ((this.p_CompileShader(t_FP,35632))!=0)){
		this.p_LinkShader();
		this.p_LinkVariables();
		c_MultiShader.m_global_uniforms=this.m_u;
	}else{
		if((this.m_shader_id)!=0){
			this.m_u=c_MultiShader.m_global_uniforms;
			this.m_active=1;
		}
	}
	return this;
}
c_MultiShader.m_new2=function(){
	c_TShaderGLSL.m_new.call(this);
	return this;
}
c_MultiShader.prototype.p_ResetShader=function(){
	return 1;
}
c_MultiShader.prototype.p_Update12=function(){
	return 0;
}
function bb_tutility_Dprint(t_x,t_y,t_z,t_a,t_b,t_c){
	var t_st=t_x+" "+t_y+" "+t_z+" "+t_a+" "+t_b+" "+t_c;
	print(t_st);
	return 0;
}
function c_FastBrightShader(){
	c_TShaderGLSL.call(this);
}
c_FastBrightShader.prototype=extend_class(c_TShaderGLSL);
c_FastBrightShader.m_init_id=0;
c_FastBrightShader.m_global_uniforms=null;
c_FastBrightShader.prototype.p_ResetShader=function(){
	if(((this.p_CompileShader("attribute vec3 aVertcoords;attribute vec2 aTexcoords0; attribute vec4 aColors; uniform mat4 pMatrix;uniform mat4 vMatrix;uniform mat4 mMatrix;varying vec2 texcoord[1]; uniform float colorflag; uniform vec4 basecolor; uniform vec2 texPosition[1],  texScale[1], texRotation[1]; uniform float texflag; varying vec4 vertColor; varying vec2 varTex; void main(){ gl_Position = (pMatrix*mMatrix ) * vec4(aVertcoords, 1.0); vertColor = mix(basecolor , aColors, colorflag); vec2 scale = texScale[0];float cosang = texRotation[0].x; float sinang = texRotation[0].y; vec2 pos = texPosition[0]/scale; (texcoord[0]).x = ((aTexcoords0.x + pos.x) * cosang - (aTexcoords0.y + pos.y) * sinang)*scale.x; (texcoord[0]).y = ((aTexcoords0.x + pos.x) * sinang + (aTexcoords0.y + pos.y) * cosang)*scale.y; varTex.x = step(1.0,texflag); varTex.y = step(2.0,texflag); \n} ",35633))!=0) && ((this.p_CompileShader("#ifdef GL_ES \n precision highp float; \n #endif \n uniform sampler2D uTexture[1]; uniform vec4 ambientcolor; uniform float alphaflag; varying vec2 texcoord[1]; varying vec4 vertColor; varying vec2 varTex; const vec4 all_ones=vec4(1.0,1.0,1.0,1.0); void main(){   vec4 tex = texture2D( uTexture[0],texcoord[0] ); if (tex.w>=alphaflag) {gl_FragColor= vec4(ambientcolor.xyz,0.0) + mix(vertColor, vertColor * tex * 1.0, varTex.x ); \n} else { discard;} \n}",35632))!=0)){
		this.p_LinkShader();
		this.p_LinkVariables();
		c_FastBrightShader.m_global_uniforms=this.m_u;
		c_FastBrightShader.m_init_id=this.m_shader_id;
		if((this.m_active)!=0){
			bb_tutility_Dprint("..FastBrightShader success","","","","","");
		}
	}else{
		return 0;
	}
	return 0;
}
c_FastBrightShader.m_new=function(){
	c_TShaderGLSL.m_new.call(this);
	this.m_MAX_TEXTURES=2;
	this.m_MAX_LIGHTS=0;
	this.m_name="FastBrightShader";
	if(c_FastBrightShader.m_init_id==0 && this.m_shader_id==0){
		this.p_ResetShader();
	}else{
		if((c_FastBrightShader.m_init_id)!=0){
			this.m_shader_id=c_FastBrightShader.m_init_id;
			this.m_u=c_FastBrightShader.m_global_uniforms;
			this.m_active=1;
		}
	}
	return this;
}
c_FastBrightShader.prototype.p_Update12=function(){
	return 0;
}
function bb_functions_CreateTexture(t_width,t_height,t_flags,t_frames){
	return c_TTexture.m_CreateTexture(t_width,t_height,t_flags,t_frames,null);
}
function c_Stack5(){
	Object.call(this);
	this.m_data=[];
	this.m_length=0;
}
c_Stack5.m_new=function(){
	return this;
}
c_Stack5.m_new2=function(t_data){
	this.m_data=t_data.slice(0);
	this.m_length=t_data.length;
	return this;
}
c_Stack5.prototype.p_ObjectEnumerator=function(){
	return c_Enumerator15.m_new.call(new c_Enumerator15,this);
}
c_Stack5.m_NIL=null;
c_Stack5.prototype.p_Length2=function(t_newlength){
	if(t_newlength<this.m_length){
		for(var t_i=t_newlength;t_i<this.m_length;t_i=t_i+1){
			this.m_data[t_i]=c_Stack5.m_NIL;
		}
	}else{
		if(t_newlength>this.m_data.length){
			this.m_data=resize_object_array(this.m_data,bb_math_Max(this.m_length*2+10,t_newlength));
		}
	}
	this.m_length=t_newlength;
}
c_Stack5.prototype.p_Length=function(){
	return this.m_length;
}
c_Stack5.prototype.p_Clear=function(){
	for(var t_i=0;t_i<this.m_length;t_i=t_i+1){
		this.m_data[t_i]=c_Stack5.m_NIL;
	}
	this.m_length=0;
}
function c_FBOStack(){
	c_Stack5.call(this);
}
c_FBOStack.prototype=extend_class(c_Stack5);
c_FBOStack.m_new=function(){
	c_Stack5.m_new.call(this);
	return this;
}
function c_Enumerator15(){
	Object.call(this);
	this.m_stack=null;
	this.m_index=0;
}
c_Enumerator15.m_new=function(t_stack){
	this.m_stack=t_stack;
	return this;
}
c_Enumerator15.m_new2=function(){
	return this;
}
c_Enumerator15.prototype.p_HasNext=function(){
	return this.m_index<this.m_stack.p_Length();
}
c_Enumerator15.prototype.p_NextObject=function(){
	this.m_index+=1;
	return this.m_stack.m_data[this.m_index-1];
}
function bb_mojographics_Transform2D(t_mat,t_x,t_y,t_z){
	var t_t2d=new_number_array(3);
	t_t2d[0]=t_mat.m_grid[0][0]*t_x+t_mat.m_grid[1][0]*t_y+t_mat.m_grid[3][0];
	t_t2d[1]=t_mat.m_grid[0][1]*t_x+t_mat.m_grid[1][1]*t_y+t_mat.m_grid[3][1];
	t_t2d[2]=t_z;
	return t_t2d;
}
function c_PreloadData(){
	Object.call(this);
	this.m_data=null;
	this.m_id=0;
}
c_PreloadData.m_new=function(){
	return this;
}
function c_ArrayIntMap(){
	Object.call(this);
	this.m_data=[];
	this.m_length=0;
}
c_ArrayIntMap.m_new=function(){
	this.m_data=new_object_array(32);
	this.m_length=31;
	return this;
}
c_ArrayIntMap.prototype.p_Get2=function(t_id){
	if(t_id<this.m_length){
		return this.m_data[t_id];
	}
	return null;
}
c_ArrayIntMap.prototype.p_Set3=function(t_id,t_obj){
	while(t_id>=this.m_length){
		this.m_length=this.m_length+32;
		this.m_data=resize_object_array(this.m_data,this.m_length+1);
	}
	this.m_data[t_id]=t_obj;
}
function bbInit(){
	bb_app__app=null;
	bb_app__delegate=null;
	bb_app__game=BBGame.Game();
	bb_graphics_device=null;
	bb_graphics_context=c_GraphicsContext.m_new.call(new c_GraphicsContext);
	c_Image.m_DefaultFlags=0;
	bb_audio_device=null;
	bb_input_device=null;
	bb_app__devWidth=0;
	bb_app__devHeight=0;
	bb_app__displayModes=[];
	bb_app__desktopMode=null;
	bb_graphics_renderDevice=null;
	c_TRender.m_shader2D=(c_BlankShader.m_new.call(new c_BlankShader));
	c_TRender.m_render=null;
	c_MojoEmulationDevice.m__device=null;
	c_MojoEmulationDevice.m__olddevice=null;
	c_MojoEmulationDevice.m__quadCache=null;
	c_TEntity.m_entity_list=c_EntityList.m_new.call(new c_EntityList);
	c_TTexture.m_useGlobalResizeSmooth=true;
	c_TRender.m_draw_list=c_List3.m_new.call(new c_List3);
	c_MojoEmulationDevice.m_firstTimeRun=false;
	c_TPixmap.m_preloader=null;
	c_Stack2.m_NIL=null;
	bb_app__updateRate=0;
	c_TRender.m_width=0;
	c_TRender.m_height=0;
	c_TCamera.m_cam_list=c_EntityList2.m_new.call(new c_EntityList2);
	c_TEntity.m_temp_mat=c_Matrix.m_new.call(new c_Matrix);
	c_TBone.m_t_mat=c_Matrix.m_new.call(new c_Matrix);
	c_TBone.m_new_mat=c_Matrix.m_new.call(new c_Matrix);
	c_TLight.m_no_lights=0;
	c_TLight.m_max_lights=8;
	c_TLight.m_light_list=c_List5.m_new.call(new c_List5);
	c_ShortBuffer.m_i2f=null;
	c_TPixmap.m_manager=null;
	c_TText.m_mask_color=0;
	c_TTextureFilter.m_filter_list=c_List8.m_new.call(new c_List8);
	c_TTexture.m_tex_list=c_List9.m_new.call(new c_List9);
	c_TTexture.m_tex_bind_stack=c_TextureStack.m_new.call(new c_TextureStack);
	c_CollisionInfo.m_t_mat=null;
	c_CollisionInfo.m_col_info=c_CollisionInfo.m_new.call(new c_CollisionInfo);
	c_TCollisionPair.m_ent_lists=new_object_array(100);
	c_CollisionInfo.m_starttime=.0;
	c_TCollisionPair.m_list=c_List10.m_new.call(new c_List10);
	c_CollisionInfo.m_nullVec=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	c_Vec3.m_temp=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	c_CollisionInfo.m_t_vec=c_Vector.m_new.call(new c_Vector,0.0,0.0,0.0);
	c_MeshCollider.m_ray_line=c_Line.m_new.call(new c_Line);
	c_Stack4.m_NIL=null;
	c_CollisionObject.m_BOXQUADS=[2,3,1,0,3,7,5,1,7,6,4,5,6,2,0,4,6,7,3,2,0,1,5,4];
	c_TRender.m_tris_rendered=0;
	c_Stack3.m_NIL=null;
	c_TShader.m_process_list=c_List13.m_new.call(new c_List13);
	c_TShader.m_g_shader=null;
	c_TRender.m_render_list=c_RenderAlphaList.m_new.call(new c_RenderAlphaList);
	c_TRender.m_render_alpha_list=c_RenderAlphaList.m_new.call(new c_RenderAlphaList);
	c_TRender.m_wireframe=0;
	c_TRender.m_temp_shader=null;
	c_TRender.m_alpha_pass=0;
	c_TRender.m_camera2D=c_TCamera.m_new.call(new c_TCamera);
	c_OpenglES20.m_last_texture=null;
	c_OpenglES20.m_last_surf=null;
	c_OpenglES20.m_last_shader=0;
	c_OpenglES20.m_last_tex_count=8;
	c_OpenglES20.m_last_effect=c_EffectState.m_new.call(new c_EffectState);
	c_TShader.m_default_shader=null;
	c_OpenglES20.m_alpha_list=c_SurfaceAlphaList.m_new.call(new c_SurfaceAlphaList);
	c_OpenglES20.m_effect=c_EffectState.m_new.call(new c_EffectState);
	c_TRender.m_vbo_enabled=false;
	c_OpenglES20.m_total_errors=0;
	c_TLight.m_ambient_red=0.1;
	c_TLight.m_ambient_green=0.1;
	c_TLight.m_ambient_blue=0.1;
	c_OpenglES20.m__usePerPixelLighting=0;
	c_FullShader.m_shader=new_object_array(10);
	c_FullShader.m_fastbrightshader=null;
	c_OpenglES20.m__nullTexture=null;
	c_FrameBufferGL.m_supportFBO=0;
	c_TEntity.m_global_mat=c_Matrix.m_new.call(new c_Matrix);
	c_FullShader.m_init_id=0;
	c_MultiShader.m_VERTVARS0="/*generic opengl 2.0 shader*/ \n#ifdef GL_ES \n precision highp float; \n#endif \nattribute vec2 aTexcoords0, aTexcoords1;attribute vec3 aVertcoords;attribute vec3 aNormals;attribute vec4 aColors;uniform mat4 pMatrix, vMatrix, mMatrix;/*light*/ uniform float lightType[2];uniform mat4 lightMatrix[2];uniform vec3 lightSpot[2]; /*x=outercutoff,y=innercutoff,z=spot exponent*/ /*color*/ uniform vec4 basecolor; uniform float colorflag, lightflag; /*texture*/ uniform vec2 texPosition[5],  texScale[5]; uniform vec2 texRotation[5]; uniform float texflag; uniform float texfxNormal[2];uniform vec3 scaleInv; uniform int fogflag; uniform vec2 fogRange; uniform float vertCoordSet[5];varying vec2 texcoord[4]; varying vec4 normal; varying vec4 vertcolor;varying vec4 lightVec, halfVec; varying float fogBlend; varying vec3 nmLight;const vec4 all_zeros = vec4(0.0,0.0,0.0,0.0);const vec4 all_ones = vec4(1.0,1.0,1.0,1.0);const float LOG2 = 1.442695;const vec2 one_zero = vec2(1.0,0.0); \n";
	c_MultiShader.m_LightVars="uniform vec4 lightColor[2];uniform vec4 lightAtt[2];const vec3 LIGHTUNIT = vec3(0.0,0.0,-1.0);uniform float shininess;";
	c_MultiShader.m_LightingEquation0="vec4 LightFunction0 ( const vec4 lightcolor, const vec3 norm, inout vec4 specular ) {const int i=0; /*do per light, webgl restriction*/float lambertTerm = 0.0; vec4 shine4 = vec4(shininess,shininess,shininess,shininess);vec3 lightPos= lightMatrix[i][3].xyz;float spotlight = 1.0;float dist = 0.0;float d=1.0;if (lightType[i] == 1.0) {lightPos= one_zero.yyy; dist = lightAtt[i].w-0.0001;\t} else if (lightType[i] == 2.0) {dist = distance(lightPos.xyz , lightVec.xyz);} else if (lightType[i] ==3.0) {dist = distance(lightPos.xyz , lightVec.xyz);mat3 lightmat = mat3(lightMatrix[i][0].xyz, lightMatrix[i][1].xyz, lightMatrix[i][2].xyz);vec3 lightDir = normalize(lightmat * LIGHTUNIT ).xyz;vec3 lightV = lightPos.xyz - lightVec.xyz; spotlight = max(-dot(normalize(lightV), lightDir), 0.0);float spotlightFade = clamp((lightSpot[i].x - spotlight) / (lightSpot[i].x - lightSpot[i].y), 0.0, 1.0);spotlight = pow(spotlight * spotlightFade, lightSpot[i].z);};\tvec3 L = ( (texflag > 0.0) && (texfxNormal[0] > 0.0) ) ? nmLight : normalize(lightPos.xyz - lightVec.xyz); vec3 N = normalize(norm); float NdotL = clamp(dot(N,L),0.0,1.0);if (NdotL > 0.0) {\tif (dist > 0.0 && dist < lightAtt[i].w*10.0) {if (lightType[i] >1.0) {d = (spotlight ) / (  lightAtt[i].x + (lightAtt[i].y* dist)  ) ;}lambertTerm = clamp(NdotL * d  , 0.0, 1.0) ;if (shininess > 0.0) {\tspecular = pow( max(dot(halfVec.xyz, N) , 0.0), 100.0  ) *  d * shine4;\t}}}return (lightColor[i] * lambertTerm  );}";
	c_MultiShader.m_VERTP1="void main() {vec4 lightPos[5]; vec4 vertVec = all_ones; lightPos[0] = vec4(lightMatrix[0][3][0],lightMatrix[0][3][1],lightMatrix[0][3][2],1.0); vec4 specular = all_zeros;/*****IMPORTANT: I'm WORKING in WORLD SPACE******* and pMatrix = p*v */vertVec = mMatrix * vec4(aVertcoords, 1.0);normal = mMatrix * vec4(aNormals,0.0);normal = normalize(normal); float light = lightType[0] * (lightflag>0.0?1.0:0.0); float d = 0.0;float spotlight = 1.0;lightVec = all_ones; /*IMPORTANT! for android, divide by 0 in normalization error*/mat3 lightmat = mat3(lightMatrix[0][0].xyz, lightMatrix[0][1].xyz, lightMatrix[0][2].xyz);/*halfvec specular*/ halfVec = vec4(normalize((lightPos[0].xyz- vertVec.xyz) + -( vMatrix[3].xyz - vertVec.xyz )) , 0.0);nmLight = all_zeros.xyz; /*meaningless*/if (light == 1.0 ) {lightVec.xyz = lightmat * vec3(0.0,0.0,-1.0); nmLight = normalize(-lightVec.xyz);} else if (light == 2.0 ) { lightVec.xyz = vertVec.xyz; nmLight = normalize(lightPos[0].xyz - vertVec.xyz);\t} else if (light == 3.0 ) { vec3 lightDir = normalize(lightmat * vec3(0.0,0.0,1.0));lightVec.xyz = vertVec.xyz; nmLight = normalize(lightPos[0].xyz - vertVec.xyz);}\n";
	c_MultiShader.m_VERTP2="";
	c_MultiShader.m_VERTP3="vertcolor = mix (basecolor, aColors, colorflag); vec4 vertpos = pMatrix * vertVec; gl_Position = vertpos; ";
	c_MultiShader.m_VERTFOG="fogBlend = 0.0;if (fogflag == 1) {\tfloat fogz = length(vertpos.xyz);fogBlend = (fogz- fogRange.x) / (fogRange.y - fogRange.x);\tfogBlend = clamp(fogBlend, 0.0, 1.0);}else if (fogflag == 2) {\tfloat fogz = length(vertpos.xyz); float dens = 1.0/ (fogRange.y - fogRange.x);\tfogBlend = 1.0-exp2( -dens*(fogz- fogRange.x)* LOG2 );fogBlend = clamp(fogBlend, 0.0, 1.0);}else if (fogflag == 3) {\tfloat fogz = length(vertpos.xyz);float ff = (fogz- fogRange.x);\tfloat dens = 1.0/ (fogRange.y - fogRange.x);fogBlend = 1.0-exp2( -dens * ff * ff * sign(ff) * LOG2 );fogBlend = clamp(fogBlend, 0.0, 1.0);}";
	c_MultiShader.m_Vert_Lighting0="lightVec = LightFunction0( all_ones, normal.xyz, specular );";
	c_MultiShader.m_FRAGVARS0="#ifdef GL_ES \nprecision highp float; \n\n#endif \nvarying vec2 texcoord[4]; varying vec4 normal;varying vec4 vertcolor;varying vec4 lightVec, halfVec; /*using z component for light att  ;spotlight coefficient packed into halfvec.w*/varying float fogBlend;varying vec3 nmLight;uniform mat4 mMatrix;/*texture*/ uniform float texflag; uniform sampler2D uTexture[5];uniform vec2 texBlend[5];uniform float texfxNormal[2];/*light*/uniform float lightflag;";
	c_MultiShader.m_FRAGVARS1="/*material*/uniform vec4 ambientcolor;uniform float flags;uniform float alphaflag; uniform vec4 fogColor;const vec2 one_zero = vec2(1.0,0.0);const vec4 all_zeros = vec4(0.0,0.0,0.0,0.0);";
	c_MultiShader.m_FRAGBLEND="/*blendfunc*/vec4 BlendFunction(const float blend, const vec4 texture, const vec4 finalcolor, const vec4 vertcolorx) {vec4 color = one_zero.yyyy;\tif (blend ==1.0) {color.xyz = mix(finalcolor.xyz, texture.xyz, texture.w );color.w = vertcolorx.a;\treturn color;} else if (blend ==2.0) { color = (vertcolorx * texture * finalcolor); \treturn color;} else if(blend==3.0) {\tcolor = (vertcolorx * texture); return finalcolor+color;} else if(blend==4.0) {\tcolor = (vertcolorx * texture); return finalcolor+color;} return (texture);}";
	c_MultiShader.m_Frag_VertexLighting0="vec4 LightFunction0 (const vec4 lightcolor, const vec3 norm, const vec4 specular ) {return lightVec;} ";
	c_MultiShader.m_FRAGP2="void main () { vec4 finalcolor = one_zero.xxxx;vec4 ambient = vec4(ambientcolor.xyz,0.0);vec4 light = one_zero.xxxx;vec4 specular = one_zero.yyyy;bool usenormalmap = (texflag > 0.0) && (texfxNormal[0] > 0.0); /*fixes webgl angle bug*/\nvec3 N = (( usenormalmap  ) ? (texture2D(uTexture[0],(texcoord[0]).xy).xyz * 2.0 - 1.0) : (normal.xyz));light = lightflag>0.0 ? LightFunction0( light, N, specular ) : one_zero.xxxx ; vec4 texture = one_zero.xxxx;\n";
	c_MultiShader.m_FRAGCOLOR="gl_FragColor = vec4(  mix( ((finalcolor.xyz * light.xyz +specular.xyz) + (finalcolor.xyz * ambient.xyz) ), fogColor.xyz, fogBlend), finalcolor.w );}";
	c_MultiShader.m_Frag_LightVars="uniform mat4 lightMatrix[2];uniform float lightType[2];uniform vec3 lightSpot[2];";
	c_MultiShader.m_global_uniforms=null;
	c_FastBrightShader.m_init_id=0;
	c_FastBrightShader.m_global_uniforms=null;
	c_FullShader.m_global_uniforms=null;
	c_FrameBufferGL.m_fboStack=c_FBOStack.m_new.call(new c_FBOStack);
	c_Stack5.m_NIL=null;
	c_FrameBufferGL.m_gsurf=null;
	c_FrameBufferGL.m_framebuffer_active=null;
	c_MojoSurface.m_list=new_string_array(0);
	c_MojoSurface.m_isLoading=false;
	c_TSprite.m_temp_mat=c_Matrix.m_new.call(new c_Matrix);
}
//${TRANSCODE_END}
